memcmp但需要将块与固定值进行比较
我需要将一块内存与C中的固定值进行比较。我可以用memcmp做这个吗? 就像是:
memcmp(starting_address, fixed_value ,num_byte)
我需要fixed_value作为固定值而不是块的起始地址。
- 将固定值写入整个临时存储器块不是一种选择,因为我的空间有限。
- 使用循环来逐个写入和检查内存不是一种选择,因为它非常慢。
如果不可能,任何人都可以告诉我一个与memcmp一样快(或更快)的解决方案吗?
谢谢,
编辑:假设我有5GB的内存,保持0。 我正在努力确保他们都是0。 检查块的第一个字节是否安全,然后执行此操作:
memcmp(starting_address,starting_address + ONE_BYTE,FIVE_GB); ?
编辑:这就是为什么我需要使用memcmp而不是用户定义的循环:
此代码运行了546个时钟周期:
memset(0x80000000 , 0x1 , 0x10000000); memset(0x90000000 , 0x1 , 0x10000000); memcmp(0x80000000 , 0x90000000 , 0x10000000);
vs这个花了7669个时钟滴答:
unsigned int i; int flag = 0; int *p = 0x80000000; int *q = 0x90000000; while(p < 0x90000000) { if(*p++ != *q++) { flag = 1; } }
我刚在Mac上测试了这个循环,它击败了memcmp
:
uint64_t *p = (uint64_t *)buffer1; uint64_t compare; memset(&compare, 1, sizeof compare); for (i = 0; i < length/sizeof compare; i++) { if (p[i] != compare) break; }
完整的示例代码:
#include #include #include #include #include #include // from: http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html void timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; } int main(int argc, char **argv) { struct rusage before; struct rusage after; struct timeval diff; size_t i; size_t length = strtoull(argv[1], NULL, 0); char *buffer1 = malloc(length); char *buffer2 = malloc(length); printf("filling..."); fflush(stdout); memset(buffer1, 1, length); memset(buffer2, 1, length); printf(" done\n"); getrusage(RUSAGE_SELF, &before); uint64_t *p = (uint64_t *)buffer1; uint64_t compare; memset(&compare, 1, sizeof compare); for (i = 0; i < length/sizeof compare; i++) { if (p[i] != compare) break; } if (i == length/sizeof compare) i = 0; getrusage(RUSAGE_SELF, &after); printf("\nloop (returned %zu):\n", i); timeval_subtract(&diff, &after.ru_utime, &before.ru_utime); printf("User: %ld.%06d s\n", diff.tv_sec, diff.tv_usec); timeval_subtract(&diff, &after.ru_stime, &before.ru_stime); printf("System: %ld.%06d s\n", diff.tv_sec, diff.tv_usec); getrusage(RUSAGE_SELF, &before); i = memcmp(buffer1, buffer2, length); getrusage(RUSAGE_SELF, &after); printf("\nmemcmp (returned %zu):\n", i); timeval_subtract(&diff, &after.ru_utime, &before.ru_utime); printf("User: %ld.%06d s\n", diff.tv_sec, diff.tv_usec); timeval_subtract(&diff, &after.ru_stime, &before.ru_stime); printf("System: %ld.%06d s\n", diff.tv_sec, diff.tv_usec); return 0; }
并运行结果:
$ make clang -Wall -Wextra -Werror -O3 -g -o example example.c ./example 0x10000000 filling... done loop (returned 0): User: 0.024078 s System: 0.000011 s memcmp (returned 0): User: 0.036752 s System: 0.000017 s
也许你可以做类似的事情?
注意:对于那些担心缓存变暖的人,我也在循环之前尝试使用memcmp并获得相同的结果。
一解决方案:
创建一个包含所有相同值的缓冲区并迭代比较。 这样,您可以获得高效的memcmp
实现的优势,而无需编写太多代码:
static char val[4096]; // tune the size of the buffer if desired /* at initialization: memset(val, 0x01, sizeof(val)) */ char *start, *ptr, *end; // initialize start and end for(ptr = start; ptr < end-sizeof(val); ptr += sizeof(val)) { if(memcmp(ptr, val, sizeof(val)) goto not_all_val; } if(memcmp(ptr, val, end - ptr)) goto not_all_val; /* all val */ puts("all val"); return; not_all_val: puts("not all val"); return;
表现比较:
10000次迭代的memcmp(buf, buf2, 4000000)
(两个长度相同的缓冲区,与问题中的第一个方法相同):
real 0m7.480s user 0m7.375s sys 0m0.103s
超过4000000字节的10000次迭代逐字符比较(与第二种方法相同):
real 0m27.004s user 0m26.908s sys 0m0.094s
超过4000000字节的上述方法的10000次迭代:
real 0m3.194s user 0m3.151s sys 0m0.042s
YMMV(我正在使用已有三年历史的Macbook Pro),但这种方法的速度是完整缓冲区的两倍 (可能是由于优越的缓存性能),而且速度几乎是逐个字符比较的十倍 。
带地址的memcmp
是比较内存块的最佳选择。 无论您是使用块内联还是使用块的内存地址,您仍然需要将块存储在某处。
您可以在编译时创建这样的块,例如:
int block[] = {3, 1, 4, 1, 5, 9};
然后在memcmp
使用block
。
如果您只是想确保将内存块设置为特定值,请使用for
循环解决方案。 你提出的任何其他解决方案都必须做同样的事情,检查整个块。
另一种选择,如果它是一个非常庞大的内存块并且耗时太长,则完全取消了这一要求。 通过这种方式,我的意思是重新设计您的算法,以便它变得不必要。 假设您有1G块。
例如:不要将它们全部设置为零。 而只是将位置设置在您正在使用的前面并保持一个单独的length
变量以指示您正在使用多少。
一个经过大量优化的memcmp
可能会胜过用户循环,但你也可能发现它没有,只是因为它必须满足一般情况 – 你检查零的特定情况可能允许编译器引入打败memcmp
优化。
与所有优化一样, 衡量,不要猜!
一种选择是从memcmp的源代码开始,并迭代地修改它以与固定缓冲区进行比较。 这样,您将保留内置于memcmp中的优化,避免外部循环的开销,并仍然实现您的目标。 您可以使用以下function:
int memcmp2(const void *s1, size_t n1, const void *s2, size_t n2);
其中n1是缓冲区s1的大小,n2是s2的大小。
如果您无法控制谁写入该内存块,那么就不可能存在允许与单个值进行有效比较的智能算法。 您将需要迭代整个块,您甚至不能跳过一个单词。 您唯一能做的就是一次比较更多数据,可能使用可以一次处理多个单词的机器指令。
如果您确实可以控制该内存并且只有您可以写入内存,那么您可以更聪明地确定其中的内容。 例如,通过“浪费”一些空间来保持一个位模式,该位模式确定哪些单词是零。 例如,如果你的单词是32位,那么你将有一个单独的内存块,你可以保留与实际内存块中的单词总和相同位数的单词。 在这种情况下,这将花费你每32字节可用内存1个字节,这并不可怕。 如果你真的需要字节粒度,那么成本要高得多:每8个。但是你通常不需要它; 一旦找到一个未归零的单词,就可以缩小搜索范围,并在第一个非零字节中搜索该单词。
如果在运行memcmp()之后,为什么你会期望内存更改? 如果内存只属于您的进程,则不会修改它。 如果这是共享内存,则问题会变得非常不同。
作为一个替代建议,我正在考虑使用memset()将所有内存放入一个已知值 – 你已经在不到546个滴答中完成了。
原因是:memset()将内存设置为一次传递中的已知值 – 通过相同的内存进行第二次传递以进行validation需要大约两倍的时间。