(机器是x86 64位运行SL6)
我试图看看我是否可以在我的64位机器上优化memset.根据我的理解,memset逐字节地设置并设置值.我假设如果我以64位为单位,它会更快.但不知何故需要更多时间.有人可以看看我的代码并提出原因吗?
/* Code */ #include <stdio.h> #include <time.h> #include <stdint.h> #include <string.h> void memset8(unsigned char *dest,unsigned char val,uint32_t count) { while (count--) *dest++ = val; } void memset32(uint32_t *dest,uint32_t val,uint32_t count) { while (count--) *dest++ = val; } void memset64(uint64_t *dest,uint64_t val,uint32_t count) { while (count--) *dest++ = val; } #define CYCLES 1000000000 int main() { clock_t start,end; double total; uint64_t loop; uint64_t val; /* memset 32 */ start = clock(); for (loop = 0; loop < CYCLES; loop++) { val = 0xDEADBEEFDEADBEEF; memset32((uint32_t*)&val,2); } end = clock(); total = (double)(end-start)/CLOCKS_PER_SEC; printf("TiMetaken memset32 %g\n",total); /* memset 64 */ start = clock(); for (loop = 0; loop < CYCLES; loop++) { val = 0xDEADBEEFDEADBEEF; memset64(&val,1); } end = clock(); total = (double)(end-start)/CLOCKS_PER_SEC; printf("TiMetaken memset64 %g\n",total); /* memset 8 */ start = clock(); for (loop = 0; loop < CYCLES; loop++) { val = 0xDEADBEEFDEADBEEF; memset8((unsigned char*)&val,8); } end = clock(); total = (double)(end-start)/CLOCKS_PER_SEC; printf("TiMetaken memset8 %g\n",total); /* memset */ start = clock(); for (loop = 0; loop < CYCLES; loop++) { val = 0xDEADBEEFDEADBEEF; memset(&val,8); } end = clock(); total = (double)(end-start)/CLOCKS_PER_SEC; printf("TiMetaken memset %g\n",total); printf("-----------------------------------------\n"); } /*Result*/ TiMetaken memset32 12.46 TiMetaken memset64 7.57 TiMetaken memset8 37.12 TiMetaken memset 6.03 -----------------------------------------
看起来标准的memset比我的实现更优化.
我试着查看代码,到处都看到memset的实现与我为memset8所做的相同.当我使用memset8时,结果更像我期望的并且与memset非常不同.
有人可以建议我做错了什么吗?
解决方法
实际的memset实现通常在汇编时手动优化,并使用目标硬件上可用的最宽对齐写入.在x86_64上将至少有16B个商店(例如,movaps).它也可能利用预取(最近这种情况不太常见,因为大多数架构都有常规访问模式的良好自动流预取器),流媒体存储或专用指令(历史上的代表在x86上速度非常慢,但最近速度相当快微体系结构).你的实现不会做这些事情.系统实现速度更快不应该是非常令人惊讶的.
例如,考虑OS X 10.8中使用的implementation(已在10.9中取代).这是适度大小缓冲区的核心循环:
.align 4,0x90 1: movdqa %xmm0,(%rdi,%rcx) movdqa %xmm0,16(%rdi,32(%rdi,48(%rdi,%rcx) addq $64,%rcx jne 1b
当以16B /周期在Haswell以前的微体系结构上命中高速缓存时,该循环将使LSU饱和.基于像memset64这样的64位存储的实现不能超过8B /周期(甚至可能无法实现,具体取决于所讨论的微体系结构以及编译器是否展开循环).在Haswell上,使用AVX存储或rep stos的实现可以更快地实现32B /周期.