将C字符串转换为所有一个案例处理器的最快方法

时间:2015-01-27 23:48:19

标签: c string assembly type-conversion cpu-registers

我在7分钟内编写的以下代码采用短字符串并将所有字母转换为小写:

void tolower(char *out,const char *in){
    int l=strlen(in);int cc;int i;
    for (i=0;i<l;i++){
        cc=(int)in[i]-0;
        if (cc >=65 && cc <=90){cc+=0x20;}
        out[i]=(char)cc;
    }
}

int main(int argc, char *argv[]){
    const char *w="aBcDe";
    char w2[6]="     ";
    tolower(w2,w);
    printf("x=%s %s\n",w,w2);
    return EXIT_SUCCESS;
}

问题在于我将处理大量数据(每秒大约10KB的数据),我希望能够创建一个尽可能快的功能。

我已经看到那些可以处理机器寄存器的代码,当我过去使用Quick Basic这样的代码时,事情运行得更快。

所以我很好奇如何在我的C程序中使用与32位和64位处理器兼容的机器寄存器(如eax)。

如果我一次只能获取字符串的至少4个字节,然后同时对所有4个字节起作用,那么这将是最好的。

在Quick Basic中,我可以借助mkd $()和cvd()函数实现我的需要。

任何人都知道如何让我发布的功能更快?请不要说升级计算机处理器。

6 个答案:

答案 0 :(得分:2)

两种方法,一种更快,取决于系统中的分析。

// tolower()
void Mike_tolower1(char *out, const char *in) {
  while ((*out++ = tolower((unsigned char) (*in++) )) != 0);
  }
}

// table lookup
void Mike_tolower2(char *out, const char *in) {
  // fill in the table
  static const char lwr[CHAR_MAX+1] = { '\0', '\1', '\2', ...
        'a', 'b' ...
        'a', 'b' ...
   };    
  while (*in) {
    *out++ = lwr[(unsigned char) (*in++)];
  }
}

答案 1 :(得分:1)

最快的方法是不要使用strlen(),因为它与下面的代码完全相同,它会计算'\0'出现之前有多少个字符,所以你要遍历字符串两次,做到这一点这样

#include <ctype.h>

void string_tolower(char *string)
{
    while (*string != '\0')
    {
        *string = tolower(*string);
        string++;
    }
}

并且不要调用你的函数tolower它是ctype.h中声明的标准函数,它将单个ascii字符转换为小写字母。

答案 2 :(得分:0)

最快取决于处理器,但速度很快的版本是:

int c;
char *cp;
for (cp = out; 0 != (c=*cp); ++cp) {
    if ((c >= 'A') && (c <= 'Z'))
        *cp = (char)(c + 'a' - 'A');
}

这包括Jester关于避免strlen的建议。

答案 3 :(得分:0)

如果要刻录一些RAM,则可以计算查找表。例如:

用于创建tolower查找表的伪代码

lookup["AA"] = "aa"
lookup["bb"] = "bb"

这样,您可以一次小写2个字节,并且不需要if语句。

如果你真的想要坚持下去,你可以写一个会尖叫的GPGPU实现。

有关在一次查询表中实现1个字符的示例,请参阅Chux答案。

答案 4 :(得分:0)

我将使用现在基于各种来源构建的代码,包括https://code.google.com/p/stringencoders/wiki/PerformanceAscii

void tolower1(char *out,const char *in,int lg){
    uint32_t x;
    const uint32_t* s = (const uint32_t*) in;
    uint32_t* d = (uint32_t*) out;
    int l=(lg/sizeof(uint32_t));
    int i;
    for(i=0;i<l;++i){
        x=s[i];
        x=x-(((x+(0x05050505+0x1a1a1a1a)) >> 2) & 0x20202020);
        d[i]=x;
    }
}

答案 5 :(得分:0)

I suggest something more like:

#include <ctype.h>


int main()
{
    const char *w="aBcDe";

    // allow room for nul termination byte
    char w2[6]="      " = {'\0'}; 

    // this 'for' statement may need tweaking if
    // w[] contains '\0' byte except at the end
    for( int i=0; w[i]; i++) 
    { 
        w3[i] = tolower(w[i]);
    }

    printf("x=%s %s\n",w,w2);
    return EXIT_SUCCESS;
} // end function: main


or, for a callable function,
that also allows for nul bytes within the string

char *myToLower( int byteCount, char *originalArray )
{
    char *lowerArray = NULL;

    if( NULL == (lowerArray = malloc(byteCount) ) )
    { // then, malloc failed
        perror( "malloc failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, malloc successful

    for( int i=0; i< byteCount; i++ )
    {
        lowerArray[i] = tolower(originalArray[i]);
    }

    return( lowerArray );
} // end function: myToLower