Faster approach to checking for an all-zero buffer in C?

后端 未结 20 2185
孤独总比滥情好
孤独总比滥情好 2020-12-03 05:33

I am searching for a faster method of accomplishing this:

int is_empty(char * buf, int size) 
{
    int i;
    for(i = 0; i < size; i++) {
        if(buf[         


        
相关标签:
20条回答
  • 2020-12-03 05:59

    I see a lot of people saying things about alignment issues preventing you from doing word sized accesses, but that's not always true. If you're looking to make portable code, then this is certainly an issue, however x86 will actually tolerate misaligned accesses. For exmaple this will only fail on the x86 if alignment checking is turned on in EFLAGS (and of course buf is actuallly not word aligned).

    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;
    }
    

    Regardless the compiler CAN convert your original loop into a loop of word-based comparisons with extra jumps to handle alignment issues, however it will not do this at any normal optimization level because it lacks information. For cases when size is small, unrolling the loop in this way will make the code slower, and the compiler wants to be conservative.

    A way to get around this is to make use of profile guided optimizations. If you let GCC get profile information on the is_empty function then re-compile it, it will be willing to unroll the loop into word-sized comparisons with an alignment check. You can also force this behavior with -funroll-all-loops

    0 讨论(0)
  • 2020-12-03 06:00

    Try checking the buffer using an int-sized variable where possible (it should be aligned).

    Off the top of my head (uncompiled, untested code follows - there's almost certainly at least one bug here. This just gives the general idea):

    /* 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;
    
    0 讨论(0)
提交回复
热议问题