ChunelFeng / CGraph

【A common used C++ DAG framework】 一个通用的、无三方依赖的、跨平台的、收录于awesome-cpp的、基于流图的并行计算框架。欢迎star & fork & 交流
http://www.chunel.cn
MIT License
1.77k stars 322 forks source link

Engine执行过程中减少局部vector的分配以提升执行性能。 #343

Closed shiyiyuedeyu closed 8 months ago

shiyiyuedeyu commented 9 months ago

https://github.com/ChunelFeng/CGraph/blob/f0078c4c107ab583e2be74e576b4c3af9ca19b81/src/GraphCtrl/GraphElement/_GEngine/GDynamicEngine/GDynamicEngine.cpp#L125

std::vector ready这个变量获取后序执行元素,对于大多数情况来说,后序关联的节点不太会是一个非常的大数。我的一个假设值为<=1000。 所有我仅是将std::vector ready改成GElementPtr ready[1000]来执行GDynamicEngine::afterElementRun()中的逻辑,对于性能测试是有帮助的。 image 这是我使用原始数组的一组测试结果。 image 这是我使用vector的一组测试结果。

虽然没有进行完整的方法设计和测试,但是可以提供一个思路。可以优化遍历后续执行元素,在元素不太大的情况下,使用栈内存(原始数组)来保存需要后续执行的元素指针。在元素过大的情况下,再使用vector来保存。

shiyiyuedeyu commented 9 months ago

另外,将element->run_before_的run_before_改成vector的容器,在此基础上还会有一些提升。 image

ChunelFeng commented 9 months ago

你好,感谢你的关注。我稍后也验证一下。

如果您对项目感兴趣,欢迎添加我个人微信 ChunelFeng,随时方便交流

ChunelFeng commented 9 months ago

你好,我看了一下你说的这个例子,得到如下结论, 在32个串行的情况下,修改后,性能提升较高; 但是在 32个并行的情况下,修改后,基本持平,甚至有一些负向优化。

我先 perf 一下,看看具体提升的原因,然后再跟你说一下。 我自己的测试环境,是 mac-m1,clion,cpp11

如果对这一块感兴趣,欢迎给我们提 pr过来

image

image

ChunelFeng commented 9 months ago

我查了一下对应的情况,在 linux上做了一些简单的性能分析,总结一下:

排查的现象

预计的原因:

如果问题 还请指正。

我们会接下来的版本中,更新这个问题。如果您对此有兴趣,也欢迎提供pr信息。

ChunelFeng commented 9 months ago

看了一下啊,应该也不是 内存的问题。两种情况下 内存占用基本没有差距。

更多的可能 就是 vector底层设计 的问题吧

shiyiyuedeyu commented 9 months ago

你好,感谢你的关注。我稍后也验证一下。

如果您对项目感兴趣,欢迎添加我个人微信 ChunelFeng,随时方便交流

你好,我看了一下你说的这个例子,得到如下结论, 在32个串行的情况下,修改后,性能提升较高; 但是在 32个并行的情况下,修改后,基本持平,甚至有一些负向优化。

我先 perf 一下,看看具体提升的原因,然后再跟你说一下。 我自己的测试环境,是 mac-m1,clion,cpp11

如果对这一块感兴趣,欢迎给我们提 pr过来

image

image

更细节原因为何对于test2有提升,是因为vector的push_back/emplace_back是一个memory load的操作,用原始数组保存指针利用栈内存避免动态分配和写入,对于std::vector<std::future> futures和std::future futures[size],这块的开销在并发流中占比不大,需要具体看性能占比。

ChunelFeng commented 9 months ago

感谢您的意见。你说的 memory load,应该指的是 vector在生成的时候,会预先分配一些信息。

by the way,能否出示一下对应的证据: 比如:memory load 的操作,导致的某项指标改变,从而导致什么影响。 或者详细一点你这样判定的依据。

shiyiyuedeyu commented 9 months ago

在arm机器上测试的 image ldr 这条指令是ARM汇编语言中的一个加载(load)指令,它的作用是从内存中加载数据到寄存器。 对应GDynamicEngine::afterElementRun()函数,对内存access比较敏感的有element->run_before_的遍历和局部std::vector ready的数据填充。

ChunelFeng commented 9 months ago

感谢您的关注。为了减少类似的逻辑,我们在最新版本中, 使用非数组(或 vector)的方式,来对即将执行的任务做管理。 并且针对单一依赖(linkable)的element,做了特判处理

实测,相对于原来的状态,在test2中,性能从7s提升到4.5s左右。

image

再次感谢您的关注和专业的意见,希望今后随时指教

ChunelFeng commented 9 months ago

另外,将element->run_before_的run_before_改成vector的容器,在此基础上还会有一些提升。 image

至于 vector or set 的问题,我们今后会考虑改动