English 中文(简体)
加快在C进行全方位缓冲检查的方法?
原标题:Faster approach to checking for an all-zero buffer in C?
  • 时间:2009-09-29 17:29:23
  •  标签:

我正在寻求一种更快的办法来做到这一点:

int is_empty(char * buf, int size) 
{
    int i;
    for(i = 0; i < size; i++) {
        if(buf[i] != 0) return 0;
    }
    return 1;
}

我认识到,除极端情况外,我没有必要寻找微量优化,但我知道存在一种更快的方法,我很想知道这种方法是什么。

最佳回答

在许多结构中,比较1个结构的时间相当于4个或8个,有时甚至16个。 通常有4个 by(或长期),8个 too(长期或长期)。 16或16岁以上可能需要在线组装,例如使用病媒单位。

而且,一个部门误判确实受到伤害,可能会有助于消除分支机构。 例如,如果缓冲几乎总是空洞的,而不是对每块零块进行检测,就把两者加在一起并测试最终结果。


表示这一点在便携式C中是困难的:将char*投到long* 违反了严格的别条。 但幸运的是,你可以使用<代码>memcpy,以方便地表达一种不结盟的、可以包括任何东西的多束弹。 编审员将优化工作,满足你们的愿望。

例如,关于Godbolt.org/z/3hXQe7”的《工作进展》(,关于Gobolt汇编商的《工作进展》()显示,你可以连续装载两个连续的“密码>;tint_fast32_t vars (64-bit)和

要使目标高效地汇编成册,而没有高效的不配负荷,就必须在启动守则中进行一些手工调整,甚至连那时,在装货时,电算法可能不会在“<条码>上>>>上,因为它可以证明符合规定。

问题回答

https://stackoverflow.com/questions/14936/faster-means-of-eck-for-an-empty-buffer-in-c/1493963#1493963” 驳回的想法:

int is_empty(char *buf, size_t size)
{
    static const char zero[999] = { 0 };
    return !memcmp(zero, buf, size > 999 ? 999 : size);
}

请注意,你可以将这一解决办法用于任意规模。 你可以这样做:

int is_empty(char *buf, size_t size)
{
    char *zero = calloc(size);
    int i = memcmp(zero, buf, size);
    free(zero);
    return i;
}

但是,任何有活力的记忆分配都将比你少。 第一个解决办法的唯一原因是它能够使用<代码>mcmp(>,该编码将由图书馆作者以组装语言进行手提,其速度将大大快于您在C中能够制定的任何内容。

EDIT:根据先前关于缓冲状态“类似”的观察,没有人提到优化: 如果一个缓冲地带没有空,在开始时或结束时,它可能不会空洞吗? 如果更可能最终制造香料,你可以最终开始检查,并可能看到业绩几乎没有提高。

EDIT 2. 由于被告在评论中:

int is_empty(char *buf, size_t size)
{
    return buf[0] == 0 && !memcmp(buf, buf + 1, size - 1);
}

这基本上将缓冲本身进行比较,初步检查看第一个要素是否为零。 这样,任何非零元素都将导致<代码>mcmp()失效。 我不知道如何比较使用另一种版本,但我确实知道,如果第一个要素是非零,那么它会很快失败(在我们甚至 lo之前)。 如果您更有可能在结尾处安装新产品,将更改<代码>buf[0]至buf[size],以取得相同效果。

四个功能用于测试一个简单基准的缓冲的零度:

#include <stdio.h> 
#include <string.h> 
#include <wchar.h> 
#include <inttypes.h> 

#define SIZE (8*1024) 
char zero[SIZE] __attribute__(( aligned(8) ));

#define RDTSC(var)  __asm__ __volatile__ ( "rdtsc" : "=A" (var)); 

#define MEASURE( func ) {  
  uint64_t start, stop;  
  RDTSC( start );  
  int ret = func( zero, SIZE );  
  RDTSC( stop );  
  printf( #func ": %s   %12"PRIu64"
", ret?"non zero": "zero", stop-start );  
} 


int func1( char *buff, size_t size ){
  while(size--) if(*buff++) return 1;
  return 0;
}

int func2( char *buff, size_t size ){
  return *buff || memcmp(buff, buff+1, size-1);
}

int func3( char *buff, size_t size ){
  return *(uint64_t*)buff || memcmp(buff, buff+sizeof(uint64_t), size-sizeof(uint64_t));
}

int func4( char *buff, size_t size ){
  return *(wchar_t*)buff || wmemcmp((wchar_t*)buff, (wchar_t*)buff+1, size/sizeof(wchar_t)-1);
}

int main(){
  MEASURE( func1 );
  MEASURE( func2 );
  MEASURE( func3 );
  MEASURE( func4 );
}

依据我原来的《刑法》:

func1: zero         108668
func2: zero          38680
func3: zero           8504
func4: zero          24768

如果你的方案只有x86,或只有x64,你可以方便地优化使用在线萨姆布。 REPE SCASD指示将扫描一个缓冲地带,直到发现非欧安会的口号。

由于没有同等的标准图书馆功能,因此不可能使用这些指示(如《苏菲尼亚法典》所确认)。

从头来看,如果你的缓冲时间是4比特(MASM syntax)的,那么就这样做了:

_asm {
   CLD                ; search forward
   XOR EAX, EAX       ; search for non-zero
   LEA EDI, [buf]     ; search in buf
   MOV ECX, [buflen]  ; search buflen bytes
   SHR ECX, 2         ; using dwords so len/=4
   REPE SCASD         ; perform scan
   JCXZ bufferEmpty:  ; completes? then buffer is 0
}

Tomas

EDIT:用Tony D 固定装置更新

简而言之,你需要看到汇编者正在产生什么法典。

$ gcc -S -O3 -o empty.s empty.c

The content of the Assembly:

        .text
        .align 4,0x90
.globl _is_empty
_is_empty:
        pushl       %ebp
        movl        %esp, %ebp
        movl        12(%ebp), %edx  ; edx = pointer to buffer
        movl        8(%ebp), %ecx   ; ecx = size
        testl       %edx, %edx
        jle L3
        xorl        %eax, %eax
        cmpb        $0, (%ecx)
        jne L5
        .align 4,0x90
L6:
        incl        %eax            ; real guts of the loop are in here
        cmpl        %eax, %edx
        je  L3
        cmpb        $0, (%ecx,%eax) ; compare byte-by-byte of buffer
        je  L6
L5:
        leave
        xorl        %eax, %eax
        ret
        .align 4,0x90
L3:
        leave
        movl        $1, %eax
        ret
        .subsections_via_symbols

这非常优化。 The loop do three matters:

  • Increase the offset
  • Compare the offset to the size
  • Compare the byte-data in memory at base+offset to 0

可以通过逐字比较来略微优化,但随后,你需要担心一致性和这种协调。

当所有其他失败时,首先,不进行猜测。

如果可能的话,利用内部变量核对缓冲(应当加以调整)。

我的头顶部(不完善、未经测试的法典)几乎肯定至少有一ug。 这只是一个总的想法:

/* check the start of the buf byte by byte while it s unaligned */
while (size && !int_aligned( buf)) {
    if (*buf != 0) {
        return 0;
    }

    ++buf;
    --size;
}


/* check the bulk of the buf int by int while it s aligned */

size_t n_ints = size / sizeof( int);
size_t rem = size / sizeof( int);

int* pInts = (int*) buf;

while (n_ints) {
    if (*pInt != 0) {
        return 0;
    }

    ++pInt;
    --n_ints;
}


/* now wrap up the remaining unaligned part of the buf byte by byte */

buf = (char*) pInts;

while (rem) {
    if (*buf != 0) {
        return 0;
    }

    ++buf;
    --rem;
}

return 1;

如果有X86,你可以使用SSE测试16个 by子:

#include "smmintrin.h" // note: requires SSE 4.1

int is_empty(const char *buf, const size_t size) 
{
    size_t i;
    for (i = 0; i + 16 <= size; i += 16)
    {
        __m128i v = _mm_loadu_si128((m128i *)&buf[i]);
        if (!_mm_testz_si128(v, v))
            return 0;
    }
    for ( ; i < size; ++i)
    {
        if (buf[i] != 0)
            return 0;
    }
    return 1;
}

这可能随着停业而得到进一步改善。

在拥有AVX的现代S86 CPU上,你甚至可以使用256倍的SIMD,在某个时候测试32台。

Hackers Delight book/site are all about Superiord C/assembly. 该网站有很好的参考文献,而且目前也相当可靠(AMD64,NUMA技术)。

查阅 rel=“nofollow noreferer”>fast memcpy,可改编为mcmp(或针对不变值的超重)。

我看到许多人说,协调问题使你无法用大话,但这并不总是真实的。 如果你重新寻找可行的守则,这当然是一个问题,但x86实际上会容忍错误的准入。 如果在ESFAGS(EFLAGS)中转而进行调和检查,这只会在x86上失败(当然,制件实际上并不一致)。

int is_empty(char * buf, int size) {
 int i;
 for(i = 0; i < size; i+= 4) {
   if(*(int *)(buf + i) != 0) {
     return 0;
   }   
 }

 for(; i < size; i++) {
   if(buf[i] != 0) 
     return 0;
 }

 return 1;
}

尽管汇编者CAN将你原先的休息时间变成了一种基于文字的比较,与额外的跳跃相适应,但是它不会在任何正常的优化水平这样做,因为它缺乏信息。 如果规模不大,那么,以这种方式使该守则放慢,汇编者希望保守。

解决这一问题的方法是利用指导性优化。 如果你让海合会了解有关“豁免功能”的信息,那么它就愿意将这种功能与“调和”核对结合起来。 你们也可以用——funroll-all-loops强迫这种行为。

是否有任何人提到 lo? 在上述任何一种循环中,循环间接费用和指数化将很大。

Also, what is the probability that the buffer will actually be empty? That s the only case where you have to check all of it. If there typically is some garbage in the buffer, the loop should stop very early, so it doesn t matter.

如果你计划将其清除为零,那么,用<条形码>(buf,0,大小(buf),无论该代码是否为零,这或许会更快。

大小到零的休息(检查):

int is_empty(char * buf, int size) 
{
    while(size --> 0) {
        if(buf[size] != 0) return 0;
    }
    return 1;
}

必须指出,我们可能无法使汇编者超前工作,因此能够最积极地优化你的编辑,并假定你可能不会更快。

a. 或处理所有使用点的人(未经过测试但可能表现良好):

int is_empty(char* buf, int size)
{
    char* org = buf;

    if (buf[size-1] == 1)
        return 0;

    buf[size-1] = 1;
    while(! *buf++);
    buf--;

    return buf == org[size-1];
}

你在你们的提问中说,你正在寻求一种最可能不必要的微观刺激。 在通常情况下,托马斯和其他人采取的阿盟办法应当给你最快的成果。

然而,这还是大局。 如果你的缓冲确实很庞大,那么从一开始就开始,进行线性搜查肯定不是这样做的最快途径。 假定你的粉刷替代物在寻找大量连续空闲区域方面非常好,但在阵列结束时却有一些非empt。 所有线性搜索都需要阅读整个阵列。 另一方面,快速激励算法可以寻找任何非零要素,并较快地压缩足够大的数据集。

因此,在做任何类型的微型刺激之前,我将仔细研究你的缓冲地带的数据,看看这是否给你带来任何模式。 对于一个单一的1条,在缓冲地带随机分配的线性搜索(处理校对/平行处理)将是最快的办法,在其他情况下不一定如此。

最初的C码的在线组装版本(如果uiSize>为= 0,和/或阵列不产生分配的例外情况)。 或许使用<代码>try{} Collection(>),因为这可能比在守则中增加许多检查更快。 或者与我一样,试图不把具有无效价值的职能(通常不可行)。 至少增加一次UNL点检查和一份size != 0检查,这是非常容易的。

 unsigned int IsEmpty(char* pchBuffer, unsigned int uiSize)
 {
    asm {
      push esi
      push ecx         

      mov esi, [pchBuffer]
      mov ecx, [uiSize]

      // add NULL ptr and size check here

      mov eax, 0

    next_char:
      repe scasb           // repeat string instruction as long as BYTE ptr ds:[ESI] == 0
                           // scasb does pointer arithmetic for BYTES (chars), ie it copies a byte to al and increments ESI by 1
      cmp cx,0             // did the loop complete?
      je all_chars_zero    // yes, array is all 0
      jmp char_not_zero    // no, loop was interrupted due to BYTE PTR ds:[ESI] != 0

    all_chars_zero:        
      mov eax, 1           // Set return value (works in MASM)
      jmp end  

    char_not_zero:
      mov eax, 0          // Still not sure if this works in inline asm

    end:
      pop ecx
      pop esi          
  }
}

这是在传单上写成的,但认为更正足够正确,是值得欢迎的。 如果有人知道如何从网上确定收益价值,请说明。

int is_empty(char * buf, int size) 
{
    int i, content=0;  
    for(i = 0; !content && i < size; i++)    
    {  
        content=content | buf(i);       // bitwise or  
    }  
    return (content==0);  
}




相关问题
热门标签