Home >Backend Development >Golang >Understand pointer operations and CPU/memory usage

Understand pointer operations and CPU/memory usage

王林
王林forward
2024-02-08 22:20:32702browse

了解指针操作和 CPU/内存使用情况

php editor Banana will introduce you to pointer operations and CPU/memory usage. In programming, pointer manipulation is a powerful tool that can directly access and modify data in memory. By understanding pointer operations, you can better control and optimize the performance of your code. In addition, understanding CPU and memory usage is also very important for optimizing programs. By monitoring and analyzing CPU and memory usage, you can identify potential performance issues and take appropriate measures to improve program operation efficiency. In this article, we will introduce you to the relevant knowledge of pointer operations and CPU/memory usage in detail to help you better understand and apply them.

Question content

I was discussing with a colleague at work whether it would be more efficient to pass a pointer to a function and/or return a pointer.

I've put together some benchmark functions to test different ways of doing this. These functions basically take a variable, convert it and pass it back. We have 4 different methods:

  1. Pass in the variable normally, create a new variable for the conversion result and pass back a copy of it
  2. Pass in the variable normally, create a new variable for the conversion result, and return the memory address
  3. Pass in a pointer to a variable, create a new variable for the conversion result and return a copy of the variable
  4. Pass in a pointer to a variable and convert the value of the pointer without returning anything.
package main

import (
    "fmt"
    "testing"
)

type mystruct struct {
    mystring string
}

func acceptparamreturnvariable(s mystruct) mystruct {
    ns := mystruct{
        fmt.sprintf("i'm quoting this: \"%s\"", s.mystring),
    }
    return ns
}

func acceptparamreturnpointer(s mystruct) *mystruct {
    ns := mystruct{
        fmt.sprintf("i'm quoting this: \"%s\"", s.mystring),
    }
    return &ns
}

func acceptpointerparamreturnvariable(s *mystruct) mystruct {
    ns := mystruct{
        fmt.sprintf("i'm quoting this: \"%s\"", s.mystring),
    }
    return ns
}

func acceptpointerparamnoreturn(s *mystruct) {
    s.mystring = fmt.sprintf("i'm quoting this: \"%s\"", s.mystring)
}

func benchmarknormalparamreturnvariable(b *testing.b) {
    s := mystruct{
        mystring: "hello world",
    }
    var ns mystruct
    for i := 0; i < b.n; i++ {
        ns = acceptparamreturnvariable(s)
    }
    _ = ns
}

func benchmarknormalparamreturnpointer(b *testing.b) {
    s := mystruct{
        mystring: "hello world",
    }
    var ns *mystruct
    for i := 0; i < b.n; i++ {
        ns = acceptparamreturnpointer(s)
    }
    _ = ns
}

func benchmarkpointerparamreturnvariable(b *testing.b) {
    s := mystruct{
        mystring: "hello world",
    }
    var ns mystruct
    for i := 0; i < b.n; i++ {
        ns = acceptpointerparamreturnvariable(&s)
    }
    _ = ns
}

func benchmarkpointerparamnoreturn(b *testing.b) {
    s := mystruct{
        mystring: "hello world",
    }
    for i := 0; i < b.n; i++ {
        acceptpointerparamnoreturn(&s)
    }
    _ = s
}

I found the results quite surprising.

$ go test -run=XXXX -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: XXXX
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
BenchmarkNormalParamReturnVariable-16           10538138               103.3 ns/op            48 B/op          2 allocs/op
BenchmarkNormalParamReturnPointer-16             9526380               201.2 ns/op            64 B/op          3 allocs/op
BenchmarkPointerParamReturnVariable-16           7542066               147.0 ns/op            48 B/op          2 allocs/op
BenchmarkPointerParamNoReturn-16                   45897            119265 ns/op          924351 B/op          5 allocs/op

Before running this, I thought the most efficient way would be the fourth test, since no new variables are created within the scope of the function being called and only the memory address is passed, however, it seems that the fourth is efficient The one with the lowest takes the most time and uses the most memory.

Can someone explain this to me, or provide me with some good reading links that explain this?

Solution

The benchmark you did does not answer the question you asked. It turns out that microbenchmarking is extremely difficult - not just in the go world, but in general.

Back to the issue of efficiency. Normally, passing pointers to functions is not escaped to the heap. Normally, pointers returned from functions do escape to the heap. Usually is the key word here. You can't really tell when the compiler allocates something on the stack and when it allocates something on the heap. This is no small problem. A very good short explanation can be found here.

But if you need to know, you can ask. You can start by simply printing the optimization decisions made by the compiler. You can do this by passing the m flag to the go tool compile.

go build -gcflags -m=1

If you pass an integer greater than 1, you will get more verbose output. If it doesn't give you the answers you need to optimize your program, try Analysis. It goes far beyond memory analysis.

In general, don’t worry about naive optimization decisions in your daily work. Don't get too hung up on "usually..." because in the real world, you never know. Always aim for correctness optimization first. Then only optimize for performance if you really need it and prove you need it. Don't guess, don't believe. Also, keep in mind that go is changing, so what we prove in one version won't necessarily hold true in another.

The above is the detailed content of Understand pointer operations and CPU/memory usage. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete