(机器是运行 SL6 的 x86 64 位)
我想看看我是否可以在我的 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
-----------------------------------------
看起来标准 memset 比我的实现更优化。 我尝试查看代码,到处都可以看到 memset 的实现与我为 memset8 所做的相同。当我使用 memset8 时,结果更像我所期望的,与 memset 有很大不同。 有人可以建议我做错了什么吗?
最佳答案
实际的 memset
实现通常在汇编中进行手动优化,并使用目标硬件上可用的最宽对齐写入。在 x86_64 上至少有 16B 个存储(例如 movaps
)。它还可能利用预取(这在最近不太常见,因为大多数体系结构都具有用于常规访问模式的良好自动流式预取器)、流式存储或专用指令(历史上 rep stos
在 x86 上慢得无法使用,但它在最近的微体系结构上相当快)。您的实现没有做这些事情。系统实现速度更快也就不足为奇了。
例如,考虑 implementation在 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
当以 16B/周期的速度命中 Haswell 之前的微架构上的缓存时,此循环将使 LSU 饱和。基于像您的 memset64
这样的 64 位存储的实现不能超过 8B/周期(甚至可能达不到,这取决于所讨论的微体系结构以及编译器是否展开您的循环)。在 Haswell 上,使用 AVX 存储或 rep stos
的实现可以更快并达到 32B/周期。
关于c - 我在 64 位机器上优化 memset 的尝试花费了比标准实现更多的时间。有人可以解释为什么吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20892847/