ChunelFeng / CGraph

【A common used C++ DAG framework】 一个通用的、无三方依赖的、跨平台的、收录于awesome-cpp的、基于流图的并行计算框架。欢迎star & fork & 交流

Home Page:http://www.chunel.cn

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

shiyiyuedeyu opened this issue · comments

std::vector<GElementPtr> ready; // 表示可以执行的列表信息

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

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

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

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

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

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

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

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

image

image

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

排查的现象

  • 老版本中,执行的 上下文切换次数,远多于 新版本。这是造成新版本性能较好的决定性原因
  • 具体查看一下,上下文切换的原因,全部是因为 非自愿上下文切换

预计的原因:

  • vector数据的实际内容,分配在堆上。在 test2 这个例子中,是超多次反复递归调用,递归的时候是不释放的,造成了一定的堆积
  • 可能是系统内,多次申请 vector 的时候,导致了 系统分配 vector的那一块实际内存 切换了(ps:不是 vector内部塞入多个数据,导致单个 vector对应的内存切换。因为这个例子中,每个vector,固定只写入一个数据)
  • 如果上述结论正确,也能说明,在 test3 中,修改了 vector -> array 之后,并没有什么性能提升。因为 主要原因,在递归,不在 vector or array

如果问题 还请指正。

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

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

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

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

如果您对项目感兴趣,欢迎添加我个人微信 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],这块的开销在并发流中占比不大,需要具体看性能占比。

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

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

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

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

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

image

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

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

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