我是说,我使用malloc分配了一段内存,可能是1k,也可能是20字节...
假设指针为pMem
我如何知道pMem
所引用的内容是否全部为Zero
或\0
?
我知道memcmp
,但第二个参数应该是另一个内存地址...
谢谢
由于Mark的回答引起了一些争议:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef count
#define count 1000*1000
#endif
#ifndef blocksize
#define blocksize 1024
#endif
int checkzeros(char *first, char *last) {
for (; first < last; ++first) {
if (*first != 0) return 0;
}
return 1;
}
int main() {
int i;
int zeros = 0;
#ifdef EMPTY
/* empty test loop */
for (i = 0; i < count; ++i) {
char *p = malloc(blocksize);
if (*p == 0) ++zeros;
free(p);
}
#endif
#ifdef LOOP
/* simple check */
for (i = 0; i < count; ++i) {
char *p = malloc(blocksize);
if (checkzeros(p, p + blocksize)) ++zeros;
free(p);
}
#endif
#ifdef MEMCMP
/* memcmp check */
for (i = 0; i < count; ++i) {
char *p = malloc(blocksize);
if (*p == 0 && !memcmp(p, p + 1, blocksize - 1)) ++zeros;
free(p);
}
#endif
printf("%d\n", zeros);
return 0;
}
测试结果(Cygwin,Windows XP,Core 2 Duo T7700 @ 2.4 GHz):
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DEMPTY && time ./cmploop
1000000
real 0m0.500s
user 0m0.405s
sys 0m0.000s
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DLOOP && time ./cmploop
1000000
real 0m1.203s
user 0m1.233s
sys 0m0.000s
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DMEMCMP && time ./cmploop
1000000
real 0m2.859s
user 0m2.874s
sys 0m0.015s
所以,对于我来说,memcmp所花费的时间大约是(2.8-0.4)/(1.2-0.4)=3倍长。看到其他人的结果会很有意思-我的所有malloc分配的内存都被清零,因此我每次比较都得到最坏情况下的时间。
对于更小的块(和更多的块),比较时间不太显著,但memcmp仍然较慢:
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DEMPTY -Dblocksize=20 -Dcount=10000000 && time ./cmploop
10000000
real 0m3.969s
user 0m3.780s
sys 0m0.030s
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DLOOP -Dblocksize=20 -Dcount=10000000 && time ./cmploop
10000000
real 0m4.062s
user 0m3.968s
sys 0m0.015s
$ gcc-4 cmploop.c -o cmploop -pedantic -Wall -O2 -DMEMCMP -Dblocksize=20 -Dcount=10000000 && time ./cmploop
10000000
real 0m4.391s
user 0m4.296s
sys 0m0.015s
我对此稍感惊讶。我原以为memcmp至少会竞争一下,因为我预计它会被内联并优化大小在编译时已知的情况。即使将其更改为在开始处测试int,然后进行16字节的memcmp,以避免不对齐的最坏情况,也没有明显加快速度。
int i = <something>; int j; memcpy(&j, &i, sizeof(int)); return j;
。这个memcpy被优化成了无操作,它只是在第一步中使用EAX来处理i。好吧,在这个问题中没有寄存器技巧的机会,也没有可以消除的多余变量,但我希望那种有能力做16字节同对齐的memcmp比较的人也能够在4个字里完成(再加上一点点工作来确定差异的符号,如果有的话)。或者其他的什么方法。 - Steve Jessopnew char[]
中随机垃圾),memcmp
仍然比循环字符快近2倍,但与循环整数完全相同。 - Pavel Minaev如果你正在测试它,并且只有在它为零时才使用它,那么请注意你会遇到竞争条件,因为@Mark Byers建议的方法没有原子测试/设置操作。在这种情况下,很难正确地得出逻辑。
如果你想要在它不为零时将其清零,那么直接将它设置为零会更快。
C ++解决方案:
bool all_zeroes =
(find_if( pMem, pMem+len, bind2nd(greater<unsigned char>(), 0)) == (pMem+len));
memcmp
用于比较一块内存和另一块内存。如果您有另一块您已经知道全是零的内存块,那么您可以使用该参考块与候选块进行比较,以查看它们是否匹配。bool is_all_zero(char const* mem, size_t size)
{
while (size-- > 0)
if (*mem++)
return false;
return true;
}
对于大缓冲区:
typedef int tNativeInt; // __int64 on 64 bit platforms with 32 bit ints
// test most of the buffer using CPU Word size
tNativeInt * ptr = reinterpret_cast<tNativeInt *>(buffer);
tNativeInt * end = ptr + bufSize / sizeof(tNativeInt);
for(;ptr < end;++ptr)
if (*ptr != 0)
return false;
// check remainder
char * ptrc = reinterpret_cast<char *>(ptr);
char * endc = ptrc + bufSize % sizeof(tNativeInt);
for(; ptrc<endc; ++ptrc)
if (*ptrc != 0)
return false;
int checkzeros(char *f, char *l) {
char a = 0;
while (f < l) {
a |= *f++;
}
if (a) {
return 0;
}
return 1;
}
核心循环中没有分支,速度约快50%。所有错误都是我的。
[编辑]:正如Steve所指出的那样,这个版本有一个好的最坏情况和一个可怕的最佳情况(因为它们是相同的)。只有在完全清零的缓冲区是唯一需要快速的情况下才使用它。
std::find_if
- 一个简单的循环检查每个字节sizeof(int)
整除,因此不太通用。但我添加了它以证明使用合理的块大小比玩弄memcpy和逐字节比较更有用。#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <windows.h>
enum {
count = 1000*1000,
blocksize = 1024
};
bool test_simple_loop(char* p){
for (int i = 0; i < blocksize; ++i) {
if (p[i] != 0) { return false; }
}
return true;
}
bool test_memcmp_clever(char* p){
return *p == 0 && memcmp(p, p + 1, blocksize - 1) == 0;
}
bool test_memcmp_naive(char* p, char* ref){
return memcmp(p, ref, blocksize) == 0;
}
struct cmp {
template <typename T>
bool operator()(T& x) {
return x != 0;
}
};
bool test_find_if(char* p){
return std::find_if(p, p+blocksize, cmp()) == p+blocksize;
}
bool test_find_if_big(int* p){
return std::find_if(p, p+blocksize, cmp()) == p+blocksize;
}
int main() {
bool res = true;
char *p = new char[blocksize];
char *ref = new char[blocksize];
std::fill(ref, ref+blocksize, 0);
std::fill(p, p+blocksize, 0); // ensure the worst-case scenario, that we have to check the entire buffer. This will also load the array into CPU cache so the first run isn't penalized
DWORD times[5];
DWORD start;
start = GetTickCount();
for (int i = 0; i != count; ++i) {
res &= test_memcmp_naive(p, ref);
}
times[0] = GetTickCount() - start;
start = GetTickCount();
for (int i = 0; i != count; ++i) {
res &= test_memcmp_clever(p);
}
times[1] = GetTickCount() - start;
start = GetTickCount();
for (int i = 0; i != count; ++i) {
res &= test_find_if(p);
}
times[2] = GetTickCount() - start;
start = GetTickCount();
for (int i = 0; i != count; ++i) {
res &= test_simple_loop(p);
}
times[3] = GetTickCount() - start;
start = GetTickCount();
for (int i = 0; i != count; ++i) {
res &= test_find_if_big(reinterpret_cast<int*>(p));
}
times[4] = GetTickCount() - start;
delete[] p;
delete[] ref;
printf("%d\n", res);
printf("%d\n%d\n%d\n%d\n%d\n", times[0], times[1], times[2], times[3], times[4]);
}
Naive memcmp: 546
"Clever" memcmp: 530
`find_if<char>`: 1466
Simple loop: 1358
`find_if<int`>: 343
std::find_if
本质上与简单循环算法相同,因此我将其作为“更简单的循环,具有更大的字长”的代表。 - jalf如果你想在分配的内存中全部为0,可以使用calloc。
memset()
将其设置为零即可;先检查它是否为零是浪费时间的,因为它很可能不是零,并且您最终会做更多的工作来进行检查和设置。memcmp()
比较。例如,您可以分配并清零一个内存块,然后保留指向它的指针以用于与其他内存块进行比较。memcmp()
实现可能会使用更有效的方法,比较整个机器字而不是单个字节。 - Wyzard
memcmp
函数(针对两个具有不同对齐方式的缓冲区),以便进行两倍所需的内存访问,适用于一个内存受限任务... 真是个好主意! - Pascal Cuoq