Предполагая, что вы имеете в виду однопотоковую пропускную способность, это довольно просто, например, вот так:
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 ГБ / с, и я знаю, что однопоточная полоса пропускания должна составлять около 20 ГБ / с, так что это нормально. Многопоточная пропускная способность на моей машине на самом деле выше, около 30 ГБ / с, но для этого потребуется более сложный тест, который координирует как минимум два потока.
Некоторые трюки необходимы в этом тесте. Самое главное, я полагаюсь на размер строки кэша 64 байта, чтобы можно было пропускать любые действия с большей частью данных. Поскольку код затрагивает каждую строку кэша (возможно, минус один или два в начале и конце из-за того, что массив не обязательно выровнен по 64), весь массив будет перенесен из памяти. На всякий случай, если это имело значение (это немного изменило результаты, поэтому я сохранил его), я развернул цикл на 4 и сделал индексную переменную без знака, чтобы избежать бессмысленных movsx
инструкций. Сохранение операций, особенно с таким скалярным кодом, важно для того, чтобы не делать этим узким местом, а не пропускную способность памяти.
Однако на самом деле это не сравнит общую пропускную способность памяти, доступной для системы, что в моей системе невозможно из одного ядра. Существуют определенные микроархитектурные детали, которые могут ограничить пропускную способность памяти одним ядром, чтобы она была меньше, чем общая пропускная способность памяти, которую имеет весь процессор. Вы можете прочитать о различных деталях в этом ответе от BeeOnRope.