计算字符串中字符发生次数的最有效方法

时间:2012-09-12 18:20:50

标签: c performance character

我正在写一个非常简单的函数,它计算某个字符在给定字符串中出现的次数。我有一个工作功能,但想知道是否有更有效或首选的方法来做到这一点。

这是功能:

size_t strchroc(const char *str, const char ch)
{ 
    int c = 0, i = 0;

    while(str[i]) if(str[i++] == ch) c++;
    return c;
}

我个人想不出任何方法可以让这段代码更有效率。如果有人知道如何使这个功能更有效率,那就是想知道(仅仅是为了学习)。

(从速度和使用最少资源的角度来看,效率很高)。

6 个答案:

答案 0 :(得分:5)

首先,除非您的功能真的时间敏感,否则请勿尝试过度优化。只需使用您提供的那个,因为它很容易验证是否正确,并且它不会试图变得聪明只是为了它。

如果功能真的需要快速,那么有很多方法可以更好地优化它。很多很多方面。它们中的一些要么期望或假设你所拥有的字符串的特定存储器布局(例如,它们被分配在字边界上,并且分配也总是填充到字边界)。因此,您需要小心,因为算法可能会对处理器,编译器和内存分配器的某些组合起作用,而对其他组合则会失败。

只是为了它,我会列出一些加速角色计数器的可能方法:

  • 一次读取字符串(32或64位整数)。由于L1缓存和推测/无序执行,不一定有很多帮助。这需要对最后一个字进行循环结束调整(在NUL终结符之后错误计算字节)。仅用于字对齐和填充内存分配器。
  • 删除条件,然后计算所有字符(到数组)的计数并返回所需字符的计数。 (这将删除条件,如果您事先知道字符串长度,则可以实现优秀的循环展开,并删除一个条件分支点。)
  • 如果您事先知道字符串的长度(在其他地方计算),您可以使用它来展开循环。或者更好,将其写为for循环并应用合适的#pragma和编译器选项,使编译器为您循环展开。
  • 在汇编程序中编写例程。 以这种方式进行之前,先启动所有编译器优化并首先反汇编程序 - 你可能会发现编译器已经使用了你知道的所有潜在技巧和你没有使用过的几个技巧。
  • 如果您的字符串可能非常大(兆字节) - 我在这里猜测 - 通过OpenCL / CUDA使用显卡可能会提供一些潜力。

等等。

但我真的,真的建议如果你遇到现实世界的问题,你会坚持使用那个。如果这是一个玩具问题,并且您正在优化它的乐趣,请继续。

循环剃须是一种学习CPU和指令集的有趣方式,但对于99.999999%的编程任务来说,这是不值得的。

答案 1 :(得分:2)

您可以使用指针迭代字符串,只需稍加努力,每个字符只使用*一次:

size_t strchroc(const char *str, const char ch)
{ 
    size_t c = 0;
    char n;
    while ((n=*str++), ((n==ch)? ++c : 0), n)
        ;
    return c;
}

并不是说编译器无法优化你的代码,只是为了好玩。

答案 2 :(得分:1)

在使用您的功能之前,您应该使用strchr()(或memchr(),如果您知道长度)。如果匹配,您可以从第一个匹配字符的位置开始,然后从那里开始。

除非你的字符串非常短,或者它很早就匹配,否则这应该快得多。

答案 3 :(得分:0)

你可以摆脱变量i

size_t strchroc(const char *str, const char ch){ 
    size_t c = 0;
    while(*str != '\0') {
        if(*str == ch) c++;
        str++;
    }
    return c;
}

答案 4 :(得分:0)

size_t count_the_string(const char *str, const char ch){
    size_t cnt ;
    for(cnt=0; *str; ) {
        cnt += *str++ == ch;
    }
    return cnt;
}

对于等效的do { ...} while();变体,GCC生成的代码没有条件跳转(当然除了循环的跳转),与@hakattack的解决方案相当。

size_t count_the_string2(const char *str, const char ch){
    size_t cnt=0 ;
    do {
        cnt += *str == ch;
    } while (*str++);
    return cnt;
}

答案 5 :(得分:0)

在快速低质量基准测试之后,我最终得到了任意长度的字符串。

在巨大的字符串(100M +)上,它没有表现出太大的差异,但在较短的字符串(句子,普通文本文件等)上,改善率约为25%。

unsigned int countc_r(char *buf, char c)
{
    unsigned int k = 0;

    for (;;) {
        if (!buf[0]) break;
        if ( buf[0] == c) ++k;
        if (!buf[1]) break;
        if ( buf[1] == c) ++k;
        if (!buf[2]) break;
        if ( buf[2] == c) ++k;
        if (!buf[3]) break;
        if ( buf[3] == c) ++k;
        buf += 4;
    }

    return k;
}
相关问题