问候贵族社区,
我想要以下循环:
for(i = 0; i < MAX; i++)
A[i] = B[i] + C[i];
这将在使用线程的共享内存四核计算机上并行运行。对于这些线程要执行的代码,正在考虑以下两种选择,其中线程tid
的ID是:0、1、2或3。
(为简单起见,假设MAX
为4的倍数)
for(i = tid; i < MAX; i += 4)
A[i] = B[i] + C[i];
for(i = tid*(MAX/4); i < (tid+1)*(MAX/4); i++)
A[i] = B[i] + C[i];
我的问题是,是否有一种方法比另一种方法更有效,为什么?
第二个比第一个更好。简单的答案:第二个最小化错误共享
现代CPU不会将字节一一加载到缓存中。它在称为缓存行的批处理中读取一次。当两个线程试图修改同一高速缓存行上的不同变量时,一个线程必须在修改一个高速缓存后重新加载它。
什么时候会发生?
基本上,内存中附近的元素将位于同一缓存行中。因此,数组中的相邻元素将位于同一缓存行中,因为数组只是一块内存。并且foo1和foo2可能也位于同一缓存行中,因为它们在同一类中定义为紧密。
class Foo {
private int foo1;
private int foo2;
}
虚假分享有多糟糕?
我从处理器缓存效果库中引用示例6
private static int[] s_counter = new int[1024]; private void UpdateCounter(int position) { for (int j = 0; j < 100000000; j++) { s_counter[position] = s_counter[position] + 3; } }
在我的四核计算机上,如果我从四个不同的线程中调用带有参数0、1、2、3的UpdateCounter,则需要4.3秒才能完成所有线程。另一方面,如果我使用参数16,32,48,64调用UpdateCounter,则该操作将在0.28秒内完成!
如何检测虚假共享?
Linux Perf可用于检测高速缓存未命中,因此可以帮助您分析此类问题。
请参考来自CPU Cache Effects和Linux Perf的分析,使用perf从上面几乎相同的代码示例中找出L1缓存未命中:
Performance counter stats for './cache_line_test 0 1 2 3': 10,055,747 L1-dcache-load-misses # 1.54% of all L1-dcache hits
[51.24%]
Performance counter stats for './cache_line_test 16 32 48 64':
36,992 L1-dcache-load-misses # 0.01% of all L1-dcache hits [50.51%]
此处显示,没有错误共享,L1缓存命中的总数将从10,055,747下降至36,992。而且性能开销不在这里,而是在加载L2,L3高速缓存,在错误共享之后加载内存的系列中。
行业中有一些好的做法吗?
LMAX Disruptor是一个高性能线程间消息传递库,它是Apache
Storm中
工作人员内部通信的默认消息传递系统
。基础数据结构是一个简单的环形缓冲区。但是为了使其快速,它使用了许多技巧来减少错误共享。
例如,它定义了超类RingBufferPad以在RingBuffer中的元素之间创建填充:
abstract class RingBufferPad
{
protected long p1, p2, p3, p4, p5, p6, p7;
}
同样,当它为缓冲区分配内存时,它会在缓冲区的前面和后面创建填充,以便不会受到相邻存储空间中数据的影响:
this.entries = new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];
资源
您可能想更多地了解所有魔术。看看作者的其中一篇文章:剖析干扰者:为什么这么快
我已经写了这个生产者/消费者问题解决方案。它似乎在工作,而不是无限循环。我的印象是,pthread\u exit(NULL) 会让它停止,但老实说,我已经迷路了。有人能告诉我如何阻止循环的正确方向吗?
我需要编写一个扩展Thread类的应用程序。我的类在实例化时接受一个整数(即100)<代码>(MyThread myt=新的MyThread(100);) 这个整数将是这个类循环并打印消息的次数。消息应该是“线程正在运行…100”。100将是我传入构造函数的任何数字。如果数字是150,那么输出应该是“线程正在运行…100”。我应该使用main方法来测试这个类。在主线程中,我将启动两个线程,一个线程
问题内容: 我正在使用从队列中连续读取的线程。 就像是: 停止此线程的最佳方法是什么? 我看到两个选择: 1-由于已弃用,因此我可以实现使用原子检查条件变量的方法。 2-将死亡事件对象或类似的对象发送到队列。当线程获取死亡事件时,它退出。 我更喜欢第一种方法,但是,我不知道何时调用该方法,因为可能有某种方法进入队列,并且停止信号可能首先到达(这是不希望的)。 有什么建议? 问题答案: 该(或因为它
我正在尝试编写一个线程,该线程将在我的主程序的后台运行,并监视某些内容。在某个时候,主程序应该向线程发出安全退出的信号。下面是一个以固定间隔将本地时间写入命令行的最小示例。 当“on”变量没有通过引用传递时,此代码会编译并产生预期的结果,只是线程永远不会终止。一旦变量通过引用传递,我就会收到编译器错误 您能建议一种修复此代码的方法吗? 额外的问题:哪里出了问题,为什么它可以与std::ref一起工
所以我正在编写代码,它将解析文件夹中的多个文本文件,收集它们的信息,并将这些信息保存在两个静态列表实例变量中。信息存放的顺序并不重要,因为我最终将对其进行排序。但由于某些原因,增加线程数不会影响速度。这是我的run方法和主方法中利用多线程的部分。 如果需要额外的信息,我基本上有一个静态实例变量作为我需要浏览的文件的数组,还有一个常量是线程数(为了测试目的手动更改)。如果我有4个线程和8个文件,每个
问题内容: 这可能是我误读了我所读内容的一种情况,但是在Java中杀死线程的所有示例似乎都表明您必须发出信号以杀死自己。您不能在没有严重风险的情况下从外面杀死它。问题是,所有有关如何“礼貌地”要求线程死亡的示例都有某种循环,因此您要做的就是观察每次迭代中的标志。 因此,我得到的是一个线程,该线程执行的操作仅需要一段时间(一系列SQL查询)。我当然可以在每个步骤之后进行检查,但是它们并没有处于循环中