导航:首页 > 源码编译 > go编译不让cpu优化参数

go编译不让cpu优化参数

发布时间:2023-05-09 19:25:36

‘壹’ Go 语言内存管理(三):逃逸分析

Go 语言较之 C 语言一个很大的优势就是自带 GC 功能,可 GC 并不是没有代价的。写 C 语言的时候,在一个函数内声明的变量,在函数退出后会自动释放掉,因为这些变量分配在栈上。如果你期望变量的数据可以在函数退出后仍然能被访问,就需要调用 malloc 方法在堆上申请内存,如果程序不再需要这块内存了,再调用 free 方法释放掉。Go 语言不需要你主动调用 malloc 来分配堆空间,编译器会自动分析,找出需要 malloc 的变量,使用堆内存。编译器的这个分析过程就叫做逃逸分析。

所以你在一个函数中通过 dict := make(map[string]int) 创建一个 map 变量,其背后的数据是放在栈空间上还是堆空间上,是不一定的。这要看编译器分析的结果。

可逃逸分析并不是百分百准确的,它有缺陷。有的时候你会发现有些变量其实在栈空间上分配完全没问题的,但编译后程序还是把这些数据放在了堆上。如果你了解 Go 语言编译器逃逸分析的机制,在写代码的时候就可以有意识地绕开这些缺陷,使你的程序更高效。

Go 语言虽然在内存管理方面降低了编程门槛,即使你不了解堆栈也能正常开发,但如果你要在性能上较真的话,还是要掌握这些基础知识。

这里不对堆内存和栈内存的区别做太多阐述。简单来说就是, 栈分配廉价,堆分配昂贵。 栈空间会随着一个函数的结束自动释放,堆空间需要时间 GC 模块不断地跟踪扫描回收。如果对这两个概念有些迷糊,建议阅读下面 2 个文章:

这里举一个小例子,来对比下堆栈的差别:

stack 函数中的变量 i 在函数退出会自动释放;而 heap 函数返回的是对变量 i 的引用,也就是说 heap() 退出后,表示变量 i 还要能被访问,它会自动被分配到堆空间上。

他们编译出来的代码如下:

逻辑的复杂度不言而喻,从上面的汇编中可看到, heap() 函数调用了 runtime.newobject() 方法,它会调用 mallocgc 方法从 mcache 上申请内存,申请的内部逻辑前面文章已经讲述过。堆内存分配不仅分配上逻辑比栈空间分配复杂,它最致命的是会带来很大的管理成本,Go 语言要消耗很多的计算资源对其进行标记回收(也就是 GC 成本)。

Go 编辑器会自动帮我们找出需要进行动态分配的变量,它是在编译时追踪一个变量的生命周期,如果能确认一个数据只在函数空间内访问,不会被外部使用,则使用栈空间,否则就要使用堆空间。

我们在 go build 编译代码时,可使用 -gcflags '-m' 参数来查看逃逸分析日志。

以上面的两个函数为例,编译的日志输出是:

日志中的 &i escapes to heap 表示该变量数据逃逸到了堆上。

需要使用堆空间,所以逃逸,这没什么可争议的。但编译器有时会将 不需要 使用堆空间的变量,也逃逸掉。这里是容易出现性能问题的大坑。网上有很多相关文章,列举了一些导致逃逸情况,其实总结起来就一句话:

多级间接赋值容易导致逃逸

这里的多级间接指的是,对某个引用类对象中的引用类成员进行赋值。Go 语言中的引用类数据类型有 func , interface , slice , map , chan , *Type(指针) 。

记住公式 Data.Field = Value ,如果 Data , Field 都是引用类的数据类型,则会导致 Value 逃逸。这里的等号 = 不单单只赋值,也表示参数传递。

根据公式,我们假设一个变量 data 是以下几种类型,相应的可以得出结论:

下面给出一些实际的例子:

如果变量值是一个函数,函数的参数又是引用类型,则传递给它的参数都会逃逸。

上例中 te 的类型是 func(*int) ,属于引用类型,参数 *int 也是引用类型,则调用 te(&j) 形成了为 te 的参数(成员) *int 赋值的现象,即 te.i = &j 会导致逃逸。代码中其他几种调用都没有形成 多级间接赋值 情况。
同理,如果函数的参数类型是 slice , map 或 interface{} 都会导致参数逃逸。

匿名函数的调用也是一样的,它本质上也是一个函数变量。有兴趣的可以自己测试一下。

只要使用了 Interface 类型(不是 interafce{} ),那么赋值给它的变量一定会逃逸。因为 interfaceVariable.Method() 先是间接的定位到它的实际值,再调用实际值的同名方法,执行时实际值作为参数传递给方法。相当于 interfaceVariable.Method.this = realValue

向 channel 中发送数据,本质上就是为 channel 内部的成员赋值,就像给一个 slice 中的某一项赋值一样。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 类型都会导致发送到 channel 中的数据逃逸。

这本来也是情理之中的,发送给 channel 的数据是要与其他函数分享的,为了保证发送过去的指针依然可用,只能使用堆分配。

可变参数如 func(arg ...string) 实际与 func(arg []string) 是一样的,会增加一层访问路径。这也是 fmt.Sprintf 总是会使参数逃逸的原因。

例子非常多,这里不能一一列举,我们只需要记住分析方法就好,即,2 级或更多级的访问赋值会 容易 导致数据逃逸。这里加上 容易 二字是因为随着语言的发展,相信这些问题会被慢慢解决,但现阶段,这个可以作为我们分析逃逸现象的依据。

下面代码中包含 2 种很常规的写法,但他们却有着很大的性能差距,建议自己想下为什么。

Benchmark 和 pprof 给出的结果:

熟悉堆栈概念可以让我们更容易看透 Go 程序的性能问题,并进行优化。

多级间接赋值会导致 Go 编译器出现不必要的逃逸,在一些情况下可能我们只需要修改一下数据结构就会使性能有大幅提升。这也是很多人不推荐在 Go 中使用指针的原因,因为它会增加一级访问路径,而 map , slice , interface{} 等类型是不可避免要用到的,为了减少不必要的逃逸,只能拿指针开刀了。

大多数情况下,性能优化都会为程序带来一定的复杂度。建议实际项目中还是怎么方便怎么写,功能完成后通过性能分析找到瓶颈所在,再对局部进行优化。

‘贰’ go编译so无法使用

1、首先,检查动态库文件嫌滚燃是否正确安装,其次,检查编译选项是否正确,比如-shared参数是否被备歼正确设置。
2、最后,追踪运行时出现的导致加载失败芹虚的错误,可能是某个符号没有被找到或者版本不匹配等情况。

‘叁’ 232.【go 语言】PProf 的使用——CPU和内存占用分析(二)

项目更目录下亏渣蠢执行 go tool pprof http://127.0.0.1:2022/debug/pprof/profile?seconds=30 ,结束之后会默认进入 PProf 的命梁纯令行交销陪互模式,接着输入 top 10 ,如下图,

项目根目录下执行 go tool pprof http://127.0.0.1:2022/debug/pprof/heap ,结束之后会默认进入 PProf 的命令行交互模式,接着输入 top ,如图所示,

上面可以看到, main.main.fun1 的 cum 大小正好等于自身的 flat 大小加上 main.Add 大小的 flat 大小

‘肆’ golang短连接导致cpu高

复杂的组合。golang短连接导致cpu高,使用golang进行复杂的组合运算,导致CPU占用率非常高晌滑。CPU指中央处理器,中央处理袜谨凳器告旅作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。

‘伍’ golang多线程简单逻辑

实现指定个核心最大化使用,比如核心总数减一。

必要的库。

要使用的cpu数量,建议不全使用。

建立管道。
声明使用的cpu数。
建立互斥关系,本例中主要为了实现所有线程执行完后再执行后续程序。

创建cpu数减1个线程

后面每个任务结束时要done一个wg,这里根据具体情况加,是循环就在每个循环里加,保证后面能全部done即可
没有缓冲的、阻塞式的往管道传递字符串。
Wait是等所有线程都执行完,即增加的数字被全done掉。
关闭管道。

假设已有的函数是ReadLogs,在它的基础上加个Wg加函数名的新函数,我觉得这种方式不改变原有的,比较舒服。
大意是:循环从管道读取字符串,读不到了就跳出循环。
每个ReadLogs()之后加一个wg.Done(),相当于计数减一。

ReadLogs()就是要执行的任务,不再解释。

就是开指定个线程。
管道阻塞传值。
wg同步。
WgReadLogs循环接收。

‘陆’ cpu数量限制 golang

runtime.NumCPU()

‘柒’ Go 是怎么使用 Go 来编译自身的

  1. Go是一个图灵完备的语言

  2. 任何图灵完备的语言理论上都可以用来编译自身。比如c/c++, java, vb, php等等都可以。

至于怎么编译自身的:

  1. 用其它语言比如c++实现一个[Go语言编译器-1]

  2. 用Go语言写一个[Go语言编译器-2]

  3. 用这个c++实现的[Go语言编译器-1]编译第2步里面说的Go语言写的[Go语言编译器-2]

  4. 用第3步得到的[Go语言编译器-2],再编译一次第2步里面说的[Go语言编译器-2]的源码

  5. OK,现在有一个Go语言实现的编译器了,最开始那个c++写的编译器没用了,可以扔掉不要了。以后就不停的优化使用这个Go语言实现的自身的编译器就行了。

‘捌’ go的while怎么不耗费cpu

线程工作频率那么高自然会使 CPU 使用率高了,你可以换个双核的或者四核的试试看!

阅读全文

与go编译不让cpu优化参数相关的资料

热点内容
如何在vps上搭建自己的代理服务器 浏览:742
nginxphp端口 浏览:403
内脏pdf 浏览:150
怎么看云服务器架构 浏览:85
我的世界国际服为什么登不进服务器 浏览:996
微盟程序员老婆 浏览:930
intellij创建java 浏览:110
java连接odbc 浏览:38
启动修复无法修复电脑命令提示符 浏览:359
手机编程是什么 浏览:98
山东移动程序员 浏览:163
苏州java程序员培训学校 浏览:477
单片机液晶驱动 浏览:854
魔拆app里能拆到什么 浏览:131
新预算法的立法理念 浏览:144
wdcpphp的路径 浏览:134
单片机p0口电阻 浏览:926
浏览器中调短信文件夹 浏览:594
五菱宏光空调压缩机 浏览:69
为什么app占用几百兆 浏览:680