首页
社区
课程
招聘
[原创]Mingw64下的性能优化版Binutils
发表于: 2023-4-21 11:56 5355

[原创]Mingw64下的性能优化版Binutils

2023-4-21 11:56
5355

  长久以来,GNU工具链的性能问题饱受争议。这也是出现gold的原因之一,连自已人都忍受不了。而在windows平台,这个问题尤为严重。虽然lld能部分替代ld,但一来lld尺寸巨大,二来总归还是和ld有不兼容的情况,手工改来改去很是麻烦。


  于是,决定动手研究一下,看有没有改进空间。花了一天时间研究binutils的代码,还真是解决了一些问题。


  所有的主要问题都集中在bfd库里。


  首先,库中对COFF数据的处理十分阳春白雪,没有半点修饰。有大量按索引定位段的逻辑,直接就是顺序查找链表。此为性能之原罪。见coffgen.c中的coff_section_from_bfd_index。


  其次,为了定位COMDAT符号,每次都会从头查找符号表。而最后仅仅是为了计算一个无关紧要的标志位。其中,为了一些结构性的设计,数据读取要经过几个抽象层次。明明在x86/x64平台上能直接读取little-endian的数据,非要绕个大圈子,一个字节一个字节去读。


  第三,不知道是故意还是无意,计算PE校验和的时候,是一个字节一个字节读取,并且每次读取前还要用seek来定位文件偏移。处理大文件时极慢。


  最后,由于没有快速定位段的手段,coff_amd64_rtype_to_howto/coff_i386_rtype_to_howto中直接硬编码从头查找链表,还在注释里表示,没有什么办法,只能硬来。


  解决办法很简单,我给bfd添加了一个按需创建的段索引表,当要通过索引定位段时,会自动创建/更新此表,然后使用这个表来定位段。coff_amd64_rtype_to_howto/coff_i386_rtype_to_howto中直接使用优化过的coff_section_from_bfd_index来定位段。计算PE校验和的逻辑也重写了一下,批量进行数据读取并计算。关于COMDAT的处理,由于牵扯很深,一时半会儿难以优化(主要是完全不熟悉binutils的代码,第一次看),但加了一个查找偏移缓存,不用每次都从头查找符号表。


  经过这一系列优化,binutil在windows上的性能得以巨大改善。如下:  



v2.40优化后说明
ranlib给库添加索引15.1s7.7s库>500MB
strip删除库的调试符号7.1s2.9s
ar创建库6.9s3.3s
ld链接exe带符号241s9.1s
ld链接exe不带符号21.7s7.8sld -s
编译我的记事本444.2s402.2s欢迎下载试用,吐槽^_^

  经过优化,编译大型带调试信息的可执行文件,性能提升了20倍以上。其它基本操作的性能也都翻倍了。以前因为调试版本的程序链接慢,一般不太用gcc进行调试,经过优化,现在已经基本可用了。


  哪怕针对整个工程的编译,由于binutils的优化,性能也提升了有10%。


  补丁已经提交给binutils项目,接不接收就不是我能控制的了。有想尝鲜的朋友可以在这下载编译好的版本,可以与msys2中的版本对比一下。


  另:BFD也是GDB加载符号的基础,这部分优化对于GDB加载大型目标慢的问题也应该有所改善,回头给GDB也打个补丁试试。


  2023-04-21补充:GDB已经验证过,性能有约20%提升。不过似乎GDB 13.1在加载巨型目标时没有印象中那么慢了,以前动则几十秒的加载时间现在变成了1秒以内。所以,这20%的提升也显得无关痛痒了。


  (内容转自我昨天发在知乎上的文章,感觉这个东西在论坛内更有价值,因而转发与此。为避免有人说我抄袭,特此备注)


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 9230
活跃值: (18670)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
2
大佬
2023-4-21 17:54
0
雪    币: 5354
活跃值: (4014)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

没有忍住,终于还是对COMDAT的处理逻辑下手了。没有改变其基本处理逻辑和框架,仅仅是针对其判断条件做了一点预处理。原本逻辑在读取符号对应的段索引时,会把整个符号转换成内部符号对象,这是为了兼容不同目标,不同平台的数据顺序及尺寸差异。但这段逻辑是针对PE COFF的处理逻辑,目标数据的尺寸和排布完全是可预期的。而当运行在little-endian平台时(现在几乎所有主流的平台都是这种了),连数据顺序都不用转换。于是,判断符号的段索引是否有效是可以直读的。之后,为了减少对原有逻辑的变更,依然都外-内部符号转换的逻辑,这里影响就会小很多。如此一来,最后一处主要性能瓶颈被解决掉。其它优化需要对整体代码逻辑进行调整,变化太大,在不熟悉代码的情况下一时半会儿怕是无法达成了。

测试结果见后表中的优化2部分


v2.40优化后(04-20)优化2(04-22)说明
ranlib给库添加索引15.1s7.7s1.9s库>500MB
strip删除库的调试符号7.1s2.9s0.9s
ar创建库6.9s3.3s1.0s
ld链接exe带符号814s103s9sDebug版
ld链接exe带符号241s9.1s2.9sRelease版
ld链接exe不带符号21.7s7.8s1.5sRelease版
编译我的记事本444.2s402.2s396sRelease版


最后于 2023-4-22 18:42 被无心红叶编辑 ,原因:
上传的附件:
2023-4-22 17:31
0
雪    币: 3535
活跃值: (31011)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2023-4-22 17:52
1
游客
登录 | 注册 方可回帖
返回
//