(机器是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, 0, 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, 0, 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, 0, 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, 0, 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
-----------------------------------------
Run Code Online (Sandbox Code Playgroud)
看起来标准的memset比我的实现更优化.我试着查看代码,到处都看到memset的实现与我为memset8所做的相同.当我使用memset8时,结果更像我期望的并且与memset非常不同.有人可以建议我做错了什么吗?
Ste*_*non 10
实际的memset
实现通常在汇编时手动优化,并使用目标硬件上可用的最宽对齐写入.在x86_64上将至少有16B个商店(movaps
例如).它也可以利用预取(最近这种情况不太常见,因为大多数架构都有常规访问模式的良好自动流预取器),流媒体存储或专用指令(历史rep stos
上在x86上速度非常慢,但在最近的微体系结构上速度相当快) .你的实现不会做这些事情.系统实现速度更快不应该是非常令人惊讶的.
例如,考虑OS X 10.8中使用的实现(已在10.9中取代).这是适度大小缓冲区的核心循环:
.align 4,0x90
1: movdqa %xmm0, (%rdi,%rcx)
movdqa %xmm0, 16(%rdi,%rcx)
movdqa %xmm0, 32(%rdi,%rcx)
movdqa %xmm0, 48(%rdi,%rcx)
addq $64, %rcx
jne 1b
Run Code Online (Sandbox Code Playgroud)
当以16B /周期在Haswell以前的微体系结构上命中高速缓存时,该循环将使LSU饱和.基于64位存储的实现,例如你memset64
不能超过8B /周期(甚至可能不会达到这个,取决于所讨论的微体系结构以及编译器是否展开你的循环).在Haswell上,一个使用AVX存储的实现,或者rep stos
可以更快地实现32B /周期.
根据我的理解,memset 逐字节进行并设置值。
该设施所做的事情的细节memset
取决于实施。依赖这一点通常是一件好事,因为我确信实现者对系统有广泛的了解,并且知道各种技术来使事情尽可能快。
为了更详细地说明一下,让我们看一下:
memset(&val, 0, 8);
Run Code Online (Sandbox Code Playgroud)
当编译器看到这一点时,它会注意到一些事情,例如:
然后根据val
或 的位置&val
(在寄存器中、在内存中……)选择要使用的正确指令。但是,如果memset
需要进行函数调用(如您的实现),那么这些优化都是不可能的。即使它无法做出编译时决策,例如:
memset(&val, x, y); // no way to tell at compile time what x and y will be...
Run Code Online (Sandbox Code Playgroud)
您可以放心,有一个用汇编程序编写的函数调用对于您的平台来说将尽可能快。