随机存储器写比随机存储器读慢吗?

时间:2019-07-15 11:55:42

标签: performance x86-64 cpu-cache memory-bandwidth

我试图找出顺序/随​​机存储器读/写的存储器访问时间。这是代码:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>

#define PRINT_EXCECUTION_TIME(msg, code)                                       \
  do {                                                                         \
    struct timeval t1, t2;                                                     \
    double elapsed;                                                            \
    gettimeofday(&t1, NULL);                                                   \
    do {                                                                       \
      code;                                                                    \
    } while (0);                                                               \
    gettimeofday(&t2, NULL);                                                   \
    elapsed = (t2.tv_sec - t1.tv_sec) * 1000.0;                                \
    elapsed += (t2.tv_usec - t1.tv_usec) / 1000.0;                             \
    printf(msg " time: %f ms\n", elapsed);                                     \
  } while (0);

const int RUNS = 20;
const int N = (1 << 27) - 1;
int *data;

int seqR() {
  register int res = 0;
  register int *data_p = data;
  register int pos = 0;

  for (register int j = 0; j < RUNS; j++) {
    for (register int i = 0; i < N; i++) {
      pos = (pos + 1) & N;
      res = data_p[pos];
    }
  }

  return res;
}

int seqW() {
  register int res = 0;
  register int *data_p = data;
  register int pos = 0;

  for (register int j = 0; j < RUNS; j++) {
    for (register int i = 0; i < N; i++) {
      pos = (pos + 1) & N;
      data_p[pos] = res;
    }
  }

  return res;
}

int rndR() {
  register int res = 0;
  register int *data_p = data;
  register int pos = 0;

  for (register int j = 0; j < RUNS; j++) {
    for (register int i = 0; i < N; i++) {
      pos = (pos + i) & N;
      res = data_p[pos];
    }
  }

  return res;
}

int rndW() {
  register int res = 0;
  register int *data_p = data;
  register int pos = 0;

  for (register int j = 0; j < RUNS; j++) {
    for (register int i = 0; i < N; i++) {
      pos = (pos + i) & N;
      data_p[pos] = res;
    }
  }

  return res;
}

int main() {
  data = (int *)malloc(sizeof(int) * N);
  assert(data);

  for (int i = 0; i < N; i++) {
    data[i] = i;
  }

  for (int i = 0; i < 10; i++) {
    PRINT_EXCECUTION_TIME("seqR", seqR());
    PRINT_EXCECUTION_TIME("seqW", seqW());
    PRINT_EXCECUTION_TIME("rndR", rndR());
    PRINT_EXCECUTION_TIME("rndW", rndW());
  }

  return 0;
}

我将gcc 6.5.0-O0结合使用来防止优化,但得到了如下结果:

seqR time: 2538.010000 ms
seqW time: 2394.991000 ms
rndR time: 40625.169000 ms
rndW time: 46184.652000 ms
seqR time: 2411.038000 ms
seqW time: 2309.115000 ms
rndR time: 41575.063000 ms
rndW time: 46206.275000 ms

很容易理解,顺序访问比随机访问要快得多。但是,对我而言,随机写入的速度比随机读取的速度慢,而顺序写入的速度却比顺序读取的速度快,对我来说没有任何意义。是什么原因引起的?

此外,我可以肯定地说seqR的内存带宽是(20 * ((1 << 27) - 1) * 4 * 1024 * 1024 * 1024)GB / (2.538)s = 4.12GB/s吗?

1 个答案:

答案 0 :(得分:3)

听起来很正常。所有x86-64 CPU(和大多数其他现代CPU)都使用回写/写分配高速缓存,因此,写入会在提交到高速缓存之前先进行一次读取,最后进行回写。

  

使用-O0来防止优化

由于您在所有本地人上使用了register,所以这是使基准测试毫无意义的罕见情况之一。

不过,您本可以在数组上使用volatile来确保这些访问中的每一个都按顺序发生,但要由优化程序决定如何实现。

  

我可以肯定地说seqR的内存带宽是(20 * ((1 << 27) - 1) * 4 * 1024 * 1024 * 1024)GB / (2.538)s = 4.12GB/s吗?

不,您的分子有2 ^ 30和10 ^ 9的额外因子。但是您做错了,无论如何都接近正确的数字。

正确的计算是每秒RUNS * N * sizeof(int) / time字节,或者 10 ^ 9 GB / s。或以2 ^ 30除以2 GiB / s。内存大小通常在GiB中,但是您可以选择带宽。 DRAM时钟速度通常约为1600 MHz,因此以理论上的最大带宽(GB / s)为单位,基本10 GB = 10 ^ 9是正常的。)

因此,基本10 GB中为4.23 GB / s。

是的,您首先初始化了数组,因此定时运行都不会触发页面错误,但是如果CPU尚未预热到最大涡轮,我可能仍会使用第二次运行。

但是请记住,这是未优化的代码。那就是您未优化的代码运行的速度,并且并不能告诉您太多内存的速度。可能是受CPU限制,而不是内存。

特别是其中有一个冗余& N,以匹配rndR/W函数的CPU工作。硬件预取可能能够保持4GB / s的速度,但每个时钟周期甚至都无法读取1 int