随想录(cpu缓存、cache同步和乱序执行)


 【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】
  
     以前学习计算机体系结构的时候,也会涉及到cpu缓存、多核和乱序方面的知识,但是真正用代码来表示相关知识的内容,却不是很多。所以,今天打算用三个例子来说明这些内容。缓存的内容比较简单,后面的内容复杂一点,不过没有关系,我们一步一步来说明。这些例子有的网上已经存在了,原来博文的作者已经不是很清楚,不过还是非常感谢他们无私的分享。闲话不多说,我们慢慢来说,
 
 (1)缓存
  #include <stdio.h>
  #include <malloc.h>
  #include <windows.h>
  
  #define PER_LINE_NUM  10000
  
  void test()
  {
  	int inner, outer, count;
      char *buffer = malloc(PER_LINE_NUM * PER_LINE_NUM);
  	if(NULL == buffer)
  	{
  		return;
  	}
  	
  	count = GetTickCount();
  	for(outer = 0; outer < PER_LINE_NUM; outer++)
  	{
  		for(inner = 0; inner < PER_LINE_NUM; inner ++)
  		{
  			buffer[outer * PER_LINE_NUM + inner] = outer;
  		}
  	}
  	printf("tick1 = %d\n", GetTickCount() - count);
  	
  	count = GetTickCount();
  	for(outer = 0; outer < PER_LINE_NUM; outer++)
  	{
  		for(inner = 0; inner < PER_LINE_NUM; inner ++)
  		{
  			buffer[outer + inner * PER_LINE_NUM] = outer;
  		}
  	}
  	
  	printf("tick2 = %d\n", GetTickCount() - count);	
  	free(buffer);
  }
  
  int main(int argc, char* argv[])
  {
  	test();
  	return 1;
  }
 

    缓存的示例比较简单一些。我们分别对一个buffer,按照行优先、列优先的顺序分别进行赋值。通过实验,我们发现行优先的数据访问模式在时间的花费上更少。其实这也没有什么奇怪的,cpu对数据的访问是按照缓存、内存的顺序逐步进行访问的。因为进行行访问时,数据有很大一部分是在cache中的,而列访问时,cpu需要不停更新自己的cache,因此时间就这么浪费了,就是这么简单。 
 
 (2)多核缓存同步

 #include <stdio.h>
 #include <windows.h>
  
  struct {
  	int a;
  	char p[64];
  	int b;
  }data;
  
  DWORD WINAPI test1(LPVOID param)
  {
  
  	int value;
  	int index;
  
  	SetThreadAffinityMask(GetCurrentThread(), 1);  
  	value = GetTickCount();
  	
  	for(index =0 ; index < 1000000000; index ++)
  	{
  		data.a++;
  	}
  
  	printf("tick1 = %d\n", GetTickCount() - value);
  	return 1;
  }
  
  DWORD WINAPI test2(LPVOID param)
  {
  
  	int value;
  	int index;
  
  	SetThreadAffinityMask(GetCurrentThread(), 2);  
  	value = GetTickCount();
  	
  	for(index =0 ; index < 1000000000; index ++)
  	{
  		data.b++;
  	}
  
  	printf("tick2 = %d\n", GetTickCount() - value);
  	return 1;
  }
  
  int main(int argc, char* argv[])
  {
  	CreateThread(NULL, 0, test1, NULL, 0, NULL);
  	CreateThread(NULL, 0, test2, NULL, 0, NULL);
  	while(1) Sleep(2);
  
  	return 1;
  }
 
    这是一个非常好玩的测试用例。我们起了两个线程,分别绑定到不同的cpu上运行。当我们对p[64]进行注释的时候,我们发现cpu需要花费较多的时间,而如果没有注释,我们发现cpu花费的时间要少得多。这就非常奇怪了。一个是对数据a进行处理、一个是对数据b进行处理,两者之间应该没有什么影响才对。关键问题就处在缓存上面。我们知道,一般cache line为64,如果数据的范围在64之内,那么cache需要在不同的cpu之间进行同步处理,而添加p[64]之后则没有这个烦恼了,此时两个cpu的cache肯定是不一样的,所以在更新一个cpu的cache的时候,不需要同步更新另外一个cpu的cache了,时间就节省下来了。
 
  (3)cpu预测
  #include <stdio.h>
  #include <malloc.h>
  #include <windows.h>
  
  typedef int (*DataCompareFunc)(void*, void*);
  #define BUFFER_SIZE 10000
  
  int simple_int_compare(void* a, void* b)
  {
  	int left = (int) a;
  	int right = (int) b;
  	
  	if(left > right)
  		return 1;
  	else if(left == right)
  		return 0;
  	else
  		return -1;
  }
  
  void quick_sort_impl(void** array, int left, int right, DataCompareFunc cmp)
  {
      int save_left  = left;
      int save_right = right;
      void* x = array[left];
  
      while(left < right)
      {
          while(cmp(array[right], x) >= 0 && left < right) right--;
          if(left != right)
          {
              array[left] = array[right];
              left++;
          }
  
          while(cmp(array[left], x) <= 0 && left < right) left++;
          if(left != right)
          {
              array[right] = array[left];
              right--;
          }
      }
  
      array[left] = x;
      if(save_left < left)
      {
          quick_sort_impl(array, save_left, left-1, cmp);
      }
  
      if(save_right > left)
      {
          quick_sort_impl(array, left+1, save_right, cmp);
      }
  
      return;
  }
  
  void quick_sort(void** array, size_t nr, DataCompareFunc cmp)
  {
      if(nr > 1)
      {
          quick_sort_impl(array, 0, nr - 1, cmp);
      }
  }
  
  void print(int array[], int size)
  {
  	int index;
  
  	for(index = 0; index < size; index++)
  	{
  		printf("%d\n", array[index]);
  	}
  }
  
  int main(int argc, char* argv[])
  {
  	int* buffer= malloc(BUFFER_SIZE * sizeof(int));
  	int index, loop, sum, value;
  
  	if(NULL == buffer)
  	{
  		return 1;
  	}
  
  	value = GetTickCount(), srand(0);
  	for(index = 0; index < BUFFER_SIZE; index++)
  	{
  		buffer[index] = rand() & 0xf;
  	}
  
  	quick_sort((void**)buffer, BUFFER_SIZE, simple_int_compare);
  	
  	sum = 0;
  	for(loop = 0; loop < BUFFER_SIZE; loop++)
  	{
  		for(index = 0; index < BUFFER_SIZE; index ++)
  		{
  			if(buffer[index] > 5)
  			{
  				sum += buffer[index];
  			}
  		}
  	}
  
  	free(buffer);
  	printf("Elapse = %d\n", GetTickCount() - value);
  
  	return 1;
  }
 
    这是我觉得最不可思议的一个范例。我们本意的目的就是把buffer中的数据找出来,可是我们发现如果对数据进行sort处理,反而降低了整个运行时间。所以要说时间节省在什么地方了?那就只有一个答案,那就是cpu乱序执行了。在cpu运行的过程,预测和乱序执行是一直存在的,我们对数据处理的时候常常会默认提前运行一些指令,而如果数据本身就是有序的,那么所有的预测都是ok的,速度本身就可以起来了,所有的判断基本上都是准确的。反之,如果本身数据无序,那么我们的预测其实是无意义的,因为一切的结果其实是随机的,所以即使cpu乱序执行了一些指令,很有可能会失败,所以提前运行的指令完全没有意义,整个运行速度也就自然而然降下来了,不知道我说清楚了没?
 
 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值