我想编写一个C#程序,能够对从主内存读取的数据运行基本操作,这样我就可以尽可能接近主内存读取带宽。
我想我们可以……
这是遵循@ harold(非常好)答案的多线程版本。
从16读取一个元素的for循环达到多重三角带宽。但实际上,循环读取所有元素的基本原因并不是很远,因为CPU瓶颈在多线程版本中不是问题。
int N = 64; uint[][] data = new uint[N][]; for (int k = 0; k < N; k++) { data[k] = new uint[1000000 * 32]; } for (int j = 0; j < 15; j++) { long total = 0; var sw = Stopwatch.StartNew(); Parallel.For(0, N, delegate (int k) { uint sum = 0; uint[] d = data[k]; //for (uint i = 0; i < d.Length; i += 64) //{ // sum += d[i] + d[i + 16] + d[i + 32] + d[i + 48]; //} for (uint i = 0; i < d.Length; i++) { sum += d[i]; } Interlocked.Add(ref total, sum); }); sw.Stop(); long dataSize = (long)data[0].Length* N * 4; Console.WriteLine("{0} {1:0.000} GB/s", total, dataSize / sw.Elapsed.TotalSeconds / (1024 * 1024 * 1024)); }
有关我笔记本电脑的信息测量:
假设你的意思是单线程带宽,这很容易,例如:
uint[] data = new uint[10000000 * 32]; for (int j = 0; j < 15; j++) { uint sum = 0; var sw = Stopwatch.StartNew(); for (uint i = 0; i < data.Length; i += 64) { sum += data[i] + data[i + 16] + data[i + 32] + data[i + 48]; } sw.Stop(); long dataSize = data.Length * 4; Console.WriteLine("{0} {1:0.000} GB/s", sum, dataSize / sw.Elapsed.TotalSeconds / (1024 * 1024 * 1024)); }
在我的机器上,我从这里获得了大约19.8-20.1 GB / s,我知道单线程带宽应该在20 GB / s左右,所以看起来很好。我的机器上的多线程带宽实际上更高,大约30 GB / s,但这需要一个更复杂的测试来协调至少两个线程。
在这个基准测试中需要一些技巧。最重要的是,我依赖64字节的缓存行大小来跳过对大多数数据做任何事情。由于代码确实触及每个高速缓存行(由于数组不一定是64对齐的,可能在开始和结束时减去一个或两个),整个数组将从内存中传输。以防它重要(它确实改变了一点结果,所以我保留了它)我将循环展开4,并使索引变量无符号以避免无意义 movsx 说明。保存操作,尤其是像这样的标量代码,对于尽量避免制作很重要 那 瓶颈,而不是内存带宽。
movsx
但是,这并没有真正对系统可用的总内存带宽进行基准测试,这在我的系统上无法从单个内核中实现。某些微体系结构细节可以将单个内核的内存带宽限制为小于整个处理器的总内存带宽。您可以阅读有关各种细节的信息 这个答案 由BeeOnRope撰写。