百科问答小站 logo
百科问答小站 font logo



相比其他语言,C、C++究竟快在哪里? 第1页

  

user avatar   yaobrother 网友的相关建议: 
      

请看《深入浅出DPDK》

这里举例一些书中提到的优化技术:

内存对齐:CPU cache 是以 cache line 为单位的。一次读取一个cache line的数据进入缓存。当多个CPU内核同时访问一个cache line中的数据时会产生伪共享(False Sharing),产生性能损失。C++中声明对齐变量可以使用 alignas(n) ,即以n字节对齐。并行编程中尽量避免同时访问一个cache line中的资源。

SIMD:Single-Instruction Multiple-Data(单指令多数据)的缩写,一个指令执行多个操作。在C++中 可以使用intrinsics 也可以使用编译器的自动向量化 ,OpenMP 的pragma omp simd提示 gcc和clang编译器都支持。

这是一段测试代码:

       #include <iostream> using namespace std;  alignas(32) int aa[64]; alignas(32) int bb[64]; alignas(32) int cc[64];  #pragma omp declare simd aligned(a,b:32) void add(const int*__restrict__  a,const int*__restrict__  b,int*__restrict__  c,int n) {  #pragma omp simd aligned(a,b:32)  for(int i=0;i<n;i++)  {   c[i]=a[i]+b[i];  } }  int main(int argc,char* argv[]) {  int count;  cin>>count;  for(int i=0;i<64;i++)  {   aa[i]=i+1;   bb[i]=i+count;  }  add(aa,bb,cc,64);   for(int i=0;i<64;i++)  {   cout<<cc[i];  }    return 0; }      

使用clang编译输出汇编文件:

       clang -O3 -S -o testomp.S -march=core-avx2 -fopenmp testomp.cpp     

main函数的汇编如下:

       main:                                   # @main .seh_proc main # %bb.0:  pushq %rsi  .seh_pushreg 6  pushq %rdi  .seh_pushreg 7  pushq %rbx  .seh_pushreg 3  subq $48, %rsp  .seh_stackalloc 48  .seh_endprologue  leaq "?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A"(%rip), %rcx  leaq 44(%rsp), %rdx  callq "??5?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@AEAH@Z"  vpbroadcastd 44(%rsp), %ymm0  vmovaps __ymm@0000000800000007000000060000000500000004000000030000000200000001(%rip), %ymm1 # ymm1 = [1,2,3,4,5,6,7,8]  vmovaps %ymm1, "?aa@@3PAHA"(%rip)  vpaddd __ymm@0000000700000006000000050000000400000003000000020000000100000000(%rip), %ymm0, %ymm1  vmovdqa %ymm1, "?bb@@3PAHA"(%rip)  vmovaps __ymm@000000100000000f0000000e0000000d0000000c0000000b0000000a00000009(%rip), %ymm1 # ymm1 = [9,10,11,12,13,14,15,16]  vmovaps %ymm1, "?aa@@3PAHA"+32(%rip)  vpaddd __ymm@0000000f0000000e0000000d0000000c0000000b0000000a0000000900000008(%rip), %ymm0, %ymm1  vmovdqa %ymm1, "?bb@@3PAHA"+32(%rip)  vmovaps __ymm@0000001800000017000000160000001500000014000000130000001200000011(%rip), %ymm1 # ymm1 = [17,18,19,20,21,22,23,24]  vmovaps %ymm1, "?aa@@3PAHA"+64(%rip)  vpaddd __ymm@0000001700000016000000150000001400000013000000120000001100000010(%rip), %ymm0, %ymm1  vmovdqa %ymm1, "?bb@@3PAHA"+64(%rip)  vmovaps __ymm@000000200000001f0000001e0000001d0000001c0000001b0000001a00000019(%rip), %ymm2 # ymm2 = [25,26,27,28,29,30,31,32]  vmovaps %ymm2, "?aa@@3PAHA"+96(%rip)  vpaddd __ymm@0000001f0000001e0000001d0000001c0000001b0000001a0000001900000018(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?bb@@3PAHA"+96(%rip)  vmovaps __ymm@0000002800000027000000260000002500000024000000230000002200000021(%rip), %ymm2 # ymm2 = [33,34,35,36,37,38,39,40]  vmovaps %ymm2, "?aa@@3PAHA"+128(%rip)  vpaddd __ymm@0000002700000026000000250000002400000023000000220000002100000020(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?bb@@3PAHA"+128(%rip)  vmovaps __ymm@000000300000002f0000002e0000002d0000002c0000002b0000002a00000029(%rip), %ymm2 # ymm2 = [41,42,43,44,45,46,47,48]  vmovaps %ymm2, "?aa@@3PAHA"+160(%rip)  vpaddd __ymm@0000002f0000002e0000002d0000002c0000002b0000002a0000002900000028(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?bb@@3PAHA"+160(%rip)  vmovaps __ymm@0000003800000037000000360000003500000034000000330000003200000031(%rip), %ymm2 # ymm2 = [49,50,51,52,53,54,55,56]  vmovaps %ymm2, "?aa@@3PAHA"+192(%rip)  vpaddd __ymm@0000003700000036000000350000003400000033000000320000003100000030(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?bb@@3PAHA"+192(%rip)  vmovaps __ymm@000000400000003f0000003e0000003d0000003c0000003b0000003a00000039(%rip), %ymm2 # ymm2 = [57,58,59,60,61,62,63,64]  vmovaps %ymm2, "?aa@@3PAHA"+224(%rip)  vpaddd __ymm@0000003f0000003e0000003d0000003c0000003b0000003a0000003900000038(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?bb@@3PAHA"+224(%rip)  vpaddd __ymm@0000000f0000000d0000000b0000000900000007000000050000000300000001(%rip), %ymm0, %ymm2  vmovdqa %ymm2, "?cc@@3PAHA"(%rip)  vpaddd __ymm@0000001f0000001d0000001b0000001900000017000000150000001300000011(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+32(%rip)  vpaddd "?aa@@3PAHA"+64(%rip), %ymm1, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+64(%rip)  vmovdqa "?bb@@3PAHA"+96(%rip), %ymm0  vpaddd "?aa@@3PAHA"+96(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+96(%rip)  vmovdqa "?bb@@3PAHA"+128(%rip), %ymm0  vpaddd "?aa@@3PAHA"+128(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+128(%rip)  vmovdqa "?bb@@3PAHA"+160(%rip), %ymm0  vpaddd "?aa@@3PAHA"+160(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+160(%rip)  vmovdqa "?bb@@3PAHA"+192(%rip), %ymm0  vpaddd "?aa@@3PAHA"+192(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+192(%rip)  vmovdqa "?bb@@3PAHA"+224(%rip), %ymm0  vpaddd "?aa@@3PAHA"+224(%rip), %ymm0, %ymm0  vmovdqa %ymm0, "?cc@@3PAHA"+224(%rip)  xorl %edi, %edi  leaq "?cc@@3PAHA"(%rip), %rbx  leaq "?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A"(%rip), %rsi  .p2align 4, 0x90     

看到了吧,循环成功的进行了向量化,优化成了AVX2指令。

NUMA:(Non-UniformMemory Architecture,非一致性内存架构), 分配内存尽可能指定在本地内存上分配。使用POSIX函数pthread_setaffinity_np设定CPU的亲和性,将线程绑定到CPU内核上,避免来回切换不同内核(可能会切换到另一个CPU上)以造成性能损失。

大页内存:一般情况下x86架构CPU使用4k内存页,不过使用一些方法可以分配2M,1G的内存页。Linux系统可以使用mount hugetlbfs 或者 MMAP加上MAP_HUGETLB参数。windows系统virtualalloc加上MEMLARGEPAGES参数。使用大页内存可以减少需要的内存页表,降低tlb miss概率,从而提升性能。例如 1G内存,x86_64架构下, 4k页表会用掉2097152字节,2M页表会用掉4096字节,而1G页表只需8字节。


user avatar   zhang-hao-72 网友的相关建议: 
      

c快在基本可以知道相应的汇编是怎么写的。




  

相关话题

  如何看待枚举类型与基本类型的关系? 
  为什么C++没有Python那么多开源库? 
  关于malloc返回值的问题? 
  为什么知乎上有的人不推荐 C 语言入门? 
  在内存特定位置填数据后,placement new 是否完全等价与cast? 
  怎么在事先未知将要输入数字的个数的情况下,将其全部赋值给一个数组? 
  intel x86指令编码存在多个选择时如何选定opcode? 
  如何通过自学找到一份开发的工作? 
  C语言初学者,想问下面这个代码哪里不对,为什么运行不了呢? 
  C++,全局变量如果用new了,需要delete吗? 

前一个讨论
有哪些在创意跟惊悚程度上比较高级的恐怖片?
下一个讨论
以GDI,WPF,win32,Qt,DX,Unity,.net为例的几组“名词”有什么联系?





© 2024-11-09 - tinynew.org. All Rights Reserved.
© 2024-11-09 - tinynew.org. 保留所有权利