代码中与字符串函数相关的优化

时间:2009-12-29 18:17:04

标签: c optimization

在调用C?中标准字符串操作相关函数之前是否有可用的指南?

例如,在调用strcmp之前,有多少优化会比较两个字符串的第一个字符(并检查它们是否相等)?

可以预期C中与字符串相关的函数有哪些类型的开销,以及哪些机制有助于避免它们?

谢谢!

11 个答案:

答案 0 :(得分:19)

字符串函数可供您使用。如果您需要比较两个字符串,请致电strcmp。不要担心微小的性能差异,无论如何都是想象的。让代码先运行。

首先,要回答有关性能的任何问题,如果你问“优化会有多少......”答案是“个人资料!”没有人能够预测某些东西的运行速度。 C stdlib的实现在中不断改进,您尝试提出的任何优化技巧都可能会对其造成伤害。

例如,我认为GCC在比较字符串时会使用矢量化,因此您实际上一次比较一些4-8个元素。你在期待吗?进行单一字符比较实际上可能会降低它的速度。

也就是说,一个典型的实现只是检查字符的字符,所以你只需要将一个比较移出循环,因为没有净增益。 (但如上所述,也许是净损失!)

所以指南是:

  

立即编程,稍后优化。

并优化理性方式:有证据和测试,而不是猜测。

答案 1 :(得分:7)

担心strcmp()大部分时间都是微观优化 - 不值得付出努力。还有其他更安全的事情,例如:

for (i = 0; i < strlen(s); i++)
{
     ...do stuff with s[i]...
}

优化器可能没有意识到它可以而且应该避免在每次循环迭代时调用函数 - 如果在循环中递增s,它可能无法避免它。这很贵。

很久以前,我在20 KB左右的缓冲区上使用strstr()等函数,程序在我开发它的HP盒子上工作正常。我将它移植到Sun机器上(请记住,这是20年前 - 问题很久以来就得到了修复),程序甚至没有爬行 - 它实际上是静止不动的。问题原来是strstr()中的一个循环,它使用strlen()或多或少,如上所示。在短缓冲区使用时,没有出现重大问题;当在20 KB缓冲区上使用时,搜索每几千字节出现的模式,可怜的机器停止运行。通过分析应用程序显示了该问题。我插入了代理strstr(),避免了这个错误,事情又恢复了正常。

使用strcat()时,另一个常见的缓慢来源是过度缓慢。例如:

strcpy(dst, name);
strcat(dst, "/subdir/");
strcat(dst, file);
strcat(dst, ".sfx");

通常,这些实际上并不是性能问题的根源 - 但在没有相反证据的情况下,它可能是缓冲区溢出的来源。您需要知道长度足够小以适应dst。但是,如果您知道每个位的长度(因为您应该确定它们适合),您可以改为编写:

strcpy(dst, name);
dst += len_name;
strcpy(dst, "/subdir/");
dst += sizeof("/subdir/") - 1;
strcpy(dst, file);
dst += len_file;
strcpy(dst, ".sfx");

这样可以在添加新材料之前反复扫描已知长度的字符串以查找结尾。短弦,这无关紧要;使用长字符串和许多串联操作,它可能很重要。和以前一样,关键是衡量成本。

Kernighan和Pike在“T he Practice of Programming”一书中有一个关于如何提高垃圾邮件过滤器性能的有趣故事。它开始在循环中使用strstr();它最终得到了一个非常不同的设计,因为strstr()设计的环境与垃圾邮件过滤系统的要求不符。但是,再次,他们只做了工作,因为它证明存在性能问题,他们做了足够的工作来防止程序成为系统的瓶颈,但不是更多。

答案 2 :(得分:4)

它不会提供任何优化,因为这正是strcmp()所做的。

通常,因为str ...()函数使用频繁,所以您可以依赖库编写器尽可能高效地实现它们。只有在您编写了自己使用这些功能的代码后,发现存在问题并通过使用配置文件跟踪它,如果您考虑编写替换项。

答案 3 :(得分:1)

研究glibc implementation of strlen

可能具有教育意义
  • 没有检查NULL参数(甚至没有断言)
  • 字符串逐字节进行比较,直到达到长字对齐的地址。之后,所有比较都以4或8字节的块完成。
  • 没有矢量化或特定于架构的东西(即没有#ifdefs)。
  • 一次比较4或8个字节时的棘手部分是找出比较失败时哪个字节为零。

答案 4 :(得分:1)

您可能对this article(Joel Spolsky)感兴趣。它涉及低级别(特别是C字符串)函数以及它们的优化方式。

答案 5 :(得分:1)

<sarcasm>

与其他答案相反,关于你的陈述:

  

例如,在调用strcmp之前,有多少优化会比较两个字符串的第一个字符(并检查它们是否相等)?

我认为这是优秀的想法。所以,我们应该这样做:

int compstr(const char *a, const char *b)
{
   if (*a == *b) return strcmp(a+1, b+1);
   else return *(unsigned char *)a < *(unsigned char *)b ? -1 : 1;
}

但是,为什么要止步呢?我们应该再检查一个字符,为我们提供更好的优化:

int compstr(const char *a, const char *b)
{
    size_t i;
    for (i=0; *a == *b && i < 2; ++a, ++b, ++i)
        if (*a == 0)
            return 0;
    if (i == 2) return strcmp(a, b);
    return *(unsigned char *)a < *(unsigned char *)b ? -1 : 1;
}

当然,我们可以做得更好。让我们将要比较的字符数作为参数:

/* Really fast implementation to compare strings,
   takes the optimization parameter n_comp */
int compstr(const char *a, const char *b, size_t n_comp)
{
    int i;
    for (i=0; *a == *b && i < n_comp; ++a, ++b, ++i)
        if (*a == 0)
            return 0;
    if (i == n_comp) return strcmp(a, b);
    return *(unsigned char *)a < *(unsigned char *)b ? -1 : 1;
}

但是如果我们要比较前几个角色的所有麻烦,为什么不自己做呢?所以,这是最终的,完全优化的版本:

/* Final, highly optimized function to compare strings */
int strcmp (const char *a, const char *b)
{
    for (; *a == *b; ++a, ++b)
        if (*a == 0)
            return 0;
    return *(unsigned char *)a < *(unsigned char *)b ? -1 : 1;
}

在编写我们的版本之后,我们很高兴地发现PJ Plauger的标准C库中的版本是identical(当然它避免了任何特定于架构的优化,一个好的图书馆会使用)!

</sarcasm>

换句话说,正如其他人所说,过早优化是没有意义的。

注意:我还没有真正检查过上面的代码片段是否正确。避免重新发明轮子的另一个原因是:你必须自己做所有艰苦的工作!

答案 6 :(得分:0)

Jonathan是绝对正确的,特别是strlen(s)示例,我通过单个stackshot找到了(在其他人的代码:-)中。

你在谈论微优化,在你有tuned the blazes out of the code之后,这是正确的担忧。在调用strcmp之前比较第一个字符会因为函数进入/退出的开销而节省一些时间,但我的经验法则是,如果对strcmp的调用花费更多,那么它是值得做的超过10%。

答案 7 :(得分:0)

标准的c-runtime字符串内容非常优化。除了利用c-runtime所不具备的有关您的问题域的知识之外,您不太可能对其进行改进。

关于预先测试第一个字符的想法有一些优点 - IFF大部分比较都在不同的字符串之间。 (即大多数都会失败)。在这种情况下,您可以避免函数调用的开销。

但你比较匹配更昂贵的字符串!

当给定匹配的字符串时,strcmp成本最高。因此,如果您的算法将传递与strcmp的两个参数相同的指针,您可以通过首先比较指针进行优化。只有你可以知道你的代码是否真的经常这样做才足以值得。

我唯一的其他一般建议是:不要使用strcat。当然它快速而简单,但使用得越多就越贵。最好跟踪字符串的结尾和strcpy到最后。

答案 8 :(得分:0)

C标准库非常好用,因为它非常优化。一些编译器内联CRT函数,因此您可以节省调用指令的开销。 但是,如果您仍然想要更快的速度,可以选择一些选项。如果您访问我给您的链接,您将能够下载一个程序,该程序包含由专业汇编语言程序员编写的几个strcmp例程。

http://www.masm32.com/board/index.php?topic=2508.0

我会特别看一下论坛成员行话写的功能。这个人写了我见过的最快的汇编代码。

如果您不知道如何在C程序中使用汇编语言函数,只需询问另一个StackOverflow问题,包括我在内的很多人都可以帮助您。

以下是将字符串abcdefg与abcz

进行比较时得到的结果
lstrcmp - original (from Microsoft) : 19314 clocks; Return value: 1
lstrcmp - kunt0r  : 957 clocks; Return value: 24832
lstrcmp - Lingo   : 501 clocks; Return value: 1

从时钟数量(越少越好)看,其他功能要快得多。

答案 9 :(得分:0)

  

在C中调用标准字符串函数之前是否有可用的指南?

是:不要担心哪些库函数比其他库函数更快或更慢,或者如何调整它们在显微镜下更快(或更慢!)。而是找到能够让您最清楚地表达意图的功能。

最后,如果您有证据表明您的应用程序太慢,您可以查看并查看字符串函数是否与您的问题有关。如果改进更可能来自像Boyer-Moore这样的次线性算法,而不是调整strcmp


Michael A. Jackson的两条优化规则:

  1. 不要这样做。

  2. (仅限专家)不要这样做。

答案 10 :(得分:0)

我认为重要的是每个与字符串相关的存储库/数据库可能都有自己的特性,可以操作或用于创建最佳的字符串操作函数。 但是,在这篇文章中,有些场合有一些简单的技巧 - 你可以选择适合你的需求并使用它: http://www.codemaestro.com/articles/21