【发布时间】:2014-11-07 18:17:09
【问题描述】:
这是一个简单的memset 带宽基准测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main()
{
unsigned long n, r, i;
unsigned char *p;
clock_t c0, c1;
double elapsed;
n = 1000 * 1000 * 1000; /* GB */
r = 100; /* repeat */
p = calloc(n, 1);
c0 = clock();
for(i = 0; i < r; ++i) {
memset(p, (int)i, n);
printf("%4d/%4ld\r", p[0], r); /* "use" the result */
fflush(stdout);
}
c1 = clock();
elapsed = (c1 - c0) / (double)CLOCKS_PER_SEC;
printf("Bandwidth = %6.3f GB/s (Giga = 10^9)\n", (double)n * r / elapsed / 1e9);
free(p);
}
在我的带有单个 DDR3-1600 内存模块的系统上(详情如下),它输出:
带宽 = 4.751 GB/s (千兆 = 10^9)
这是理论 RAM 速度的 37%:1.6 GHz * 8 bytes = 12.8 GB/s
另一方面,这里有一个类似的“读取”测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
unsigned long do_xor(const unsigned long* p, unsigned long n)
{
unsigned long i, x = 0;
for(i = 0; i < n; ++i)
x ^= p[i];
return x;
}
int main()
{
unsigned long n, r, i;
unsigned long *p;
clock_t c0, c1;
double elapsed;
n = 1000 * 1000 * 1000; /* GB */
r = 100; /* repeat */
p = calloc(n/sizeof(unsigned long), sizeof(unsigned long));
c0 = clock();
for(i = 0; i < r; ++i) {
p[0] = do_xor(p, n / sizeof(unsigned long)); /* "use" the result */
printf("%4ld/%4ld\r", i, r);
fflush(stdout);
}
c1 = clock();
elapsed = (c1 - c0) / (double)CLOCKS_PER_SEC;
printf("Bandwidth = %6.3f GB/s (Giga = 10^9)\n", (double)n * r / elapsed / 1e9);
free(p);
}
它输出:
带宽 = 11.516 GB/s (千兆 = 10^9)
我可以接近读取性能的理论极限,例如对大型数组进行异或运算,但写入似乎要慢得多。为什么?
操作系统 Ubuntu 14.04 AMD64(我用gcc -O3编译。使用-O3 -march=native会使读取性能稍差,但不影响memset)
CPU至强E5-2630 v2
RAM 单个“16GB PC3-12800 Parity REG CL11 240-Pin DIMM”(包装盒上写着什么)我认为拥有单个 DIMM 可以使性能更加可预测。我假设使用 4 个 DIMM,memset 将最多快 4 倍。
主板 Supermicro X9DRG-QF(支持4通道内存)
附加系统:一台配备 2 个 4GB DDR3-1067 RAM 的笔记本电脑:读写速度均约为 5.5 GB/s,但请注意它使用 2 个 DIMM。
P.S. 用这个版本替换 memset 得到完全相同的性能
void *my_memset(void *s, int c, size_t n)
{
unsigned long i = 0;
for(i = 0; i < n; ++i)
((char*)s)[i] = (char)c;
return s;
}
【问题讨论】:
-
printf("%4d/%4ld\r", p[0], r);在您的基准测试中意味着您很可能是在计时,而不是其他任何事情。 I/O 很慢。 -
@RetiredNinja 不!
printf在运行 20 秒的程序中被调用 101 次 -
在您发布的代码中,它应该被调用 100 次。它没有理由出现在您进行基准测试的代码部分中。
-
我在我的系统上尝试过,有无循环中的 printf。差异比我预期的要小(运行 3 次)。有,我得到 9.644、9.667 和 9.629,没有我得到 9.740、9.614 和 9.653
-
我的 2010 年旧 MacBook 报告 1.937 GB/s 没有优化,173010.381 GB/s 使用发布的代码进行优化,未修改:-) memset 最有可能写入缓存行,该缓存行首先从RAM 要缓存以便被修改,然后刷新,因此每个缓存行都是读取 + 写入的,而不仅仅是读取。剩余的差异可能是由于在非连续位置的读/写。 PowerPC 有清除缓存行的指令,这会有所帮助。
标签: c performance memory hardware