这实际上是一个编码问题。
我有一台 i7-3820 和 4 * 4GB DDR3 1600Mhz 计算机在 linux 下运行。根据英特尔的规范,我相信我可以以 51.2GB/s(不是 GiB/s)的速度扫描内存。但不幸的是,我只能获得 40GB/s。
首先,我在汇编中编写了一个内存到 xmm 加载过程。假设它被声明为
extern "C" {
void load_mem_256b(int *start, int *end, int step, int *p_sum);
}
返回值是所有加载的整数的第一个 int 的总和,以避免优化。
它将从 start 指向的内存地址加载 256bits,然后按 step * 8 (8 * sizeof(int) = 256 bits) 提前开始
我试过两种读取内存的方法,第一种是开4个线程,把内存分成4段;另一种方法是打开4个线程,让每个线程加载1024b中的第i个256b部分,并正确同步4个线程。
正如我之前提到的,第一种方法达到了 40GB/s。第二种方法速度较慢。
在第一种方法中,如果内存工作在联动模式下,不同行的内存访问量会很大。由于每个 DIMM * 4 DIMM 有 2 个等级,我不知道它是否可以在不降低性能的情况下正常工作。在第二种方法中,我想让内存加载只发生在同一行,并让不同的线程从不同的内存通道加载。
第一种方法如下所示:
for (int i = 0; i < number_of_threads; ++i)
threads[i] = std::thread(std::bind(
load_mem_256b, start + i * 8, end, number_of_threads, &(sums[i])));
第二种方法如下所示:
size_t amount = 32768;
my::spin_barrier barrier(number_of_threads + 1);
for (int i = 0; i < number_of_threads; ++i)
threads[i] = std::thread(std::bind(load_mem_256b_barrier,
start + i * 8, end,
number_of_threads,
&barrier, amount, &(sums[i])));
threads[number_of_threads] = std::thread(std::bind(
prefetch, start, end, amount, &barrier));
补充一点数据是,在第一种方法中,如果我只打开 1 个或 2 个或 3 个线程,我可以以 17GB/s、32GB/s、39GB/s 的速度加载内存。我对所有这些数字感到奇怪。如果内存工作在 unganged 模式,为什么 1 个线程可以 17GB/s 的速度加载内存?(单通道只能发送12.8GB/s) 但是如果是联动模式,为什么第二种方法比第一种方法慢很多?
最后,如何以理论速度实际加载内存?