有效主内存到 CPU C# 中的最大带宽

Effective main memory to CPU maximum bandwidth in C#

我想编写一个 C# 程序,能够对从主内存读取的数据进行 运行ning 基本操作,以便我可以尽可能接近主内存 读取带宽

我想我们可以确定在使用非常大的数组时不使用缓存。到目前为止,使用多线程和 long[] 我从来没有能够超过 2 GB/s 秒的限制,而我知道现代 RAM 带宽至少更像是 10 GB/s。 (我有一台现代计算机和 运行 64 位,当然没有调试的发布模式)。

能否提供一个能够接近最大带宽的C#程序?如果不能,您能解释一下为什么 C# 程序不能这样做吗?

例如:

假设您指的是单线程带宽,那很简单,例如:

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 指令。保存操作很重要,尤其是对于这样的标量代码,为了尽量避免使 that 成为瓶颈,而不是内存带宽。

但是,这并没有真正对系统可用的总内存带宽进行基准测试,这在我的系统上是不可能通过单核进行的。某些微架构细节可以将内存带宽限制为单个内核,使其小于整个处理器的总内存带宽。您可以在 BeeOnRope 的 中阅读各种详细信息。

这是遵循@harold(非常好的)回答的多线程版本。

for循环16取1达到多线程带宽。但实际上基本的 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));
}

关于在我的笔记本电脑上测量的信息:

  • 单线程带宽:13 GB/s
  • 多线程带宽:20GB/s
  • 多线程读取所有元素:17 GB/s