分页: 1 / 1

为何这个C程序写内存比读内存更快?

发表于 : 2017-07-26 20:48
科学之子
为何这个C程序写内存比读内存更快?

代码: 全选

//usage:
//./a.out max r/w repeat timeout
//indent ./zramtest.c;gcc -O0 ./zramtest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int
main (int argc, char **argv)
{
  time_t const start_time = time (NULL);
  time_t timeout;
  int max;
  int repeat;
  if (argc == 5)
    {
      max = atoi (argv[1]);
      repeat = atoi (argv[3]);
      timeout = ((time_t) (atoi (argv[4])));
    }
  else
    return 1;

  unsigned char **block_array =
    calloc (sizeof (unsigned char *), (size_t) (max));

  size_t block_length = (size_t) (1024u * 1024u);

  unsigned char data[3];
  data[0] = 'a';
  data[1] = 'b';
  data[2] = 'c';

  unsigned i = 0u;
  //initialize block_array
  for (i = 0u; i < max; i++)
    {
      do
	{
	  if ((timeout > ((time_t) (0)))
	      && ((time (NULL) - start_time) > timeout))
	    {
	      puts ("timeouted!");
	      return 0;
	    }
	  block_array[i] = malloc (block_length);
	  if (block_array[i] != NULL)
	    {
	      unsigned bi = 0u;
	      for (bi = 0u; bi < block_length; bi++)
		block_array[i][bi] = data[bi % ((unsigned) (sizeof (data)))];
	    }
	  else
	    {
	      printf ("%u error\n", i);
	    }
	}
      while (NULL == block_array[i]);
    }
  puts ("init ok");

  unsigned score = 0u;
//do page read test
  if ('r' == argv[2][0])
    for (;;)
      {
	for (i = 0u; i < max; i++)
	  {
	    if ((timeout > ((time_t) (0)))
		&& ((time (NULL) - start_time) > timeout))
	      {
		puts ("timeouted!");
		goto show_score;
	      }

	    unsigned bi = 0u;
	    for (bi = 0u; bi < block_length; bi++)
	      {
		data[bi % ((unsigned) (sizeof (data)))] = block_array[i][bi];
	      }
	    score++;
	  }
	if (repeat >= 0)
	  {
	    repeat--;
	    if (0 == repeat)
	      goto show_score;
	  }
      }
//do page write test
  else if ('w' == argv[2][0])
    for (;;)
      {
	for (i = 0u; i < max; i++)
	  {
	    if ((timeout > ((time_t) (0)))
		&& ((time (NULL) - start_time) > timeout))
	      {
		puts ("timeouted!");
		goto show_score;
	      }

	    unsigned bi = 0u;
	    for (bi = 0u; bi < block_length; bi++)
	      {
		block_array[i][bi] = data[bi % ((unsigned) (sizeof (data)))];
	      }
	    score++;
	  }
	if (repeat >= 0)
	  {
	    repeat--;
	    if (0 == repeat)
	      goto show_score;
	  }
      }
show_score:
  printf ("score:%u\n", score);
  return 0;
}
为了保持客观,我测试的时候没有任何swap设备,zram模块也是卸载掉的.

测试结果:

代码: 全选

$ time ./a.out 100 r  5 -1
init ok
score:500

real	0m2.692s
user	0m2.592s
sys	0m0.096s
$ time ./a.out 100 w  5 -1
init ok
score:500

real	0m2.566s
user	0m2.468s
sys	0m0.096s
$ time ./a.out 100 r  5 -1
init ok
score:500

real	0m2.691s
user	0m2.608s
sys	0m0.080s
$ time ./a.out 100 w  5 -1
init ok
score:500

real	0m2.571s
user	0m2.468s
sys	0m0.100s
$ time ./a.out 100 r  5 -1
init ok
score:500

real	0m2.698s
user	0m2.608s
sys	0m0.088s
$ time ./a.out 100 w  5 -1
init ok
score:500

real	0m2.566s
user	0m2.476s
sys	0m0.092s
$ 

Re: 为何这个C程序写内存比读内存更快?

发表于 : 2017-07-27 17:55
astolia
你这个只是在比 读堆写栈 和 读栈写堆 谁快。堆和栈都是内存,完全不是你以为的是在比 写内存 和 读内存
block_array在堆上,data在栈上,访问栈上的数据可以直接栈寄存器加偏移,理论上会比通过内存地址访问堆快一丁点。另外还因为现代CPU有流水线以及相关的优化手段存在,代码顺序的差异可能会导致执行时间差异被进一步放大。

我自己做了个测试,把

代码: 全选

block_array[i][bi] = data[bi % ((unsigned) (sizeof (data)))];
改成

代码: 全选

p = &block_array[i][bi];
q = &data[bi % ((unsigned) (sizeof (data)))];
*p = *q;
这种形式,这样两种赋值只有*p = *q和*q = *p的差别了,最终产生的机器码除开地址以外是完全一致的。这样可以尽可能减少流水线机制的干扰

在服务器的E5-2603 v2 CPU上跑测试,测出来的结果如下
1. 同样往变量中读(c = *p 和 c = *q ),从栈读比从堆读快了4%
2. 同样写入立即数(*p = 1 和 *q = 1 ),往栈写比往堆写快了1%
3. 按你的代码逻辑(*p = *q 和 *q = *p),读堆写栈 比 读栈写堆 慢了1.5%

Re: 为何这个C程序写内存比读内存更快?

发表于 : 2017-07-28 21:58
科学之子
astolia 写了:block_array在堆上,data在栈上,访问栈上的数据可以直接栈寄存器加偏移,理论上会比通过内存地址访问堆快一丁点。
您说的"访问"指的是"读"还是"写"?
@astolia

Re: 为何这个C程序写内存比读内存更快?

发表于 : 2017-08-01 22:41
astolia
我指的是CPU的内存寻址。不过你这个测试程序里既有堆也有栈,对最后结果倒是不会有影响