我想编写一个C#程序,能够对从主内存读取的数据运行基本操作,这样我就可以尽可能接近主内存读取带宽。
我想我们可以确定在使用非常大的数组时不使用缓存。到目前为止,使用多线程和长[]我从未能够超过2 GB /秒的限制,而我知道现代RAM带宽至少比10 GB / s更高。 (我有一台现代计算机并以64位运行,当然没有调试的发布模式)。
你能提供能够接近最大带宽的C#程序吗?如果没有,你能解释为什么C#程序无法做到吗?
例如:
答案 0 :(得分:3)
假设您的意思是单线程带宽,那就相当容易了,例如:
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
指令。保存操作,尤其是使用这样的标量代码,对于尽量避免使 瓶颈而不是内存带宽非常重要。
然而,这并没有真正对系统可用的总内存带宽进行基准测试,而这在我的系统中无法从单个内核中获得。某些微体系结构细节可以将单个内核的内存带宽限制为小于整个处理器的总内存带宽。您可以通过BeeOnRope阅读this answer中的各种详细信息。
答案 1 :(得分:1)
这是遵循@ 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));
}
我的笔记本电脑上的信息测量: