全局变量的顺序改变了 C++/OpenGL 中的性能
Posted
技术标签:
【中文标题】全局变量的顺序改变了 C++/OpenGL 中的性能【英文标题】:Order of global variables changes performance in C++/OpenGL 【发布时间】:2014-02-07 13:32:49 【问题描述】:我有一段多线程代码 (Windows),在 C++ 中使用 pthread 进行线程处理。
我有 4 个线程拆分单个作业,因此每个作业都访问相当大的全局数组的 1/4。
如果我在全局范围声明部分的特定点定义指向全局范围内数组的指针,我发现我的性能会降低 2 倍 - 似乎我的全局声明的顺序对我很重要应用性能。
有人知道为什么吗?
这是我的结果:
单线程 = 0.07 秒 4 个线程 = 0.02 秒(当指向大数组的全局指针位于 全局声明的开始) 4 个线程 = 0.04 秒(当指向大数组的全局指针为 在全局声明的中间)如果重要的话,我在全局范围内声明的指针是:
GLbyte* 渲染标志; GLfloat* surface_primative;
正如正确指出的那样,我应该评论说,这些测试的精度非常高,它们可能是 2 个有效数字,但它们是高度一致的。
以前有没有人遇到过基于全局声明顺序的性能差异?或者有人知道为什么理论上会发生这种情况吗?
附加:
我注意到(感谢 SO 的建议)我的线程使用的数组的数组指针声明直接位于(在内存空间中)一些其他变量的旁边,这些变量也被线程修改:
int number_of_tiles;
int success_count_1; ///Counters for successful searches
int success_count_2;
int success_count_3;
int success_count_4;
int offset;
GLbyte* render_flag; ///Pointer to array of render flags
GLfloat* surface_primative; ///Pointer to surface primative array
如果我在指向数组的指针和计数器之间添加一些填充,我的代码会再次快速运行:
int number_of_tiles;
int success_count_1; ///Counters for successful searches
int success_count_2;
int success_count_3;
int success_count_4;
int offset;
char padding[100];
GLbyte* render_flag; ///Pointer to array of render flags
GLfloat* surface_primative; ///Pointer to surface primative array
【问题讨论】:
可能有一些代码?你在说哪些声明?你不是说只有一个大数组吗? 只有一个有效数字的 0.02 秒测试不是很有说服力。您不能设置一个运行至少 15-20 秒的测试,然后将其用作基准测试吗?还要确保您明显启用优化。 不幸的是,我的应用程序中有很多全局声明,因为它对我的一些线程来说很方便,但我注意到这个指向(将成为)巨大数组的指针的特殊声明很重要它在包内的位置。我以前从未遇到过这种情况。全局变量是指向 OpenGL 数组的指针。我会把它添加到我的问题中。 测试高度一致,更长的时间范围会减少错误/CPU噪音的机会,但我希望你相信2 sig fig值是可靠的,因为它是。它运行了无数次,并且在运行之间变化了 0.001(当然,除非全局范围声明被移动)。所以它本质上不是“噪音”。 这个问题不是很清楚,但是“线程”和“当我移动变量声明时性能变化”让我脑子里的一个小人说“虚假共享”。 【参考方案1】:正如 Art 在评论中所建议的那样,我想您最终可能会得到 False Sharing,即数据共享一个缓存行,因此线程/内核无法高效工作。
您需要将每个线程写入的数据保持公平(64 到 256 字节左右)分开,以便在使用多个线程时获得良好的效率。
指针和数据的数据访问/写入模式如何?
更新
我不是专家,但可能是 1/4 距离(模高速缓存行大小)可能会使线程相互干扰。 Cache associativiy 试图减轻这种情况,但仍然如此。
另一个问题是这些指针附近的数据是否不断更新。
一个好的分析器,比如英特尔的 VTune 应该有助于诊断。
至于行为,我希望数据根据您重新排序的方式分配到不同的地址。指针周围的数据是什么样的?
我认为通过分配比您需要的稍多的内存并在合适的 n 字节边界上对齐块来缓解这种情况。如果问题不是数据,而是指针,您可以将它们对齐在 64 字节的边界上,或者在每个线程中保留它们的本地副本。
再次,我知道英特尔(在他们的 IPP 库中)有一个特定的 STL 分配器可以部分帮助解决这个问题。阅读这些内容应该会对您有所帮助。
文章“Avoiding and Identifying false sharing”是一个开始,还有一些额外的指针(呵呵;)。
【讨论】:
谢谢 Macke,这是一个真正有用的答案。我所说的大数组是一个连续的数组,4个线程通过块发送一个指向该数组中每个位置的指针 1/4 并修改每个块的 1/4。从您所说的来看,这可能是我的问题,尽管为什么只有在我的全局声明部分中的某些点定义数组声明时才会发生这种情况仍然让我感到困惑。再次感谢您的回答。 我不是专家,但可能是 1/4 距离(模高速缓存行大小)可能会使线程相互干扰。我已经用更多信息和链接修改了答案。嗨。 另外,您是更新这些指针还是只读取它们?你在那些指针附近写数据吗? (例如,如果您有一个经常更新的全局计数器或互斥锁,就在附近..) @SingleEntity 其他“全局声明”是什么?是否有一些由线程编写的全局声明变量?这就是虚假分享发生的时候。以上是关于全局变量的顺序改变了 C++/OpenGL 中的性能的主要内容,如果未能解决你的问题,请参考以下文章