【问题标题】:How to best enhance my program to take advantage of strcmp's timing optimization?如何最好地增强我的程序以利用 strcmp 的时序优化?
【发布时间】:2018-06-07 00:57:39
【问题描述】:

我目前正在做一个依赖 strcmp 时序优化的项目。例如,给定两个字符串 a1, a2 其中 a1=a2 和两个字符串 b1, b2 where b1=/=b2 我们知道 strcmp (a1,a2) 在理论上比 strcmp(b1,b2) 需要更长的时间来完成,因为 strcmp一旦它意识到第一个字符串中的一个字节不等于第二个字符串中的相应字节,这意味着当两个字符串相等时 strcmp 将花费最长的时间来完成,因为它需要遍历整个长度。我的项目目前正在使用各种字符串对 strcmp 的性能进行计时,它的成功取决于一次调用 strcmp 是否比另一次调用更快,即使正在比较的两个字符串中的一个字节是关闭的。

我创建了一个更简单的虚拟程序来隔离和测试性能(虚拟程序如下),它比较了比较两个相等字符串的性能与两个不相等字符串的性能。参考代码,当 str3="aaaaaaaaaa"(或与 str1 有很大差异的任何随机文本)时,很明显,比较两个相等字符串(str1 和 str2)的第一段比比较两个不相等字符串的第二段慢得多(str2 和 str3)。但是,当如下所示切换 str3="hellohella" 时,结果非常相似,并且确定哪个段完成得更快/更慢变得不可预测。我也尝试过使用 clock() 来为函数调用计时,但这比 rusage 更不准确。

有什么方法可以改变我的代码,使两个不相等的字符串的比较总是比两个相等的字符串的比较快(即使只有 1 个字节)?有没有比我尝试过的更准确的 C 计时器?感谢您的宝贵时间。

int main ()
{
    int iterations=10000;
    struct rusage usage;
    struct timeval start, end;

    char * str1="hellohello";
    char * str2="hellohello";
    char * str3="hellohella";
    double tempTotal=0;
    for (int i=0; i<iterations; i++){
            struct rusage usage;
            struct timeval start, end;


            getrusage(RUSAGE_SELF, &usage);
            start=usage.ru_stime;

            for (int j=0; j<100000; j++) strcmp(str1, str2);

            getrusage(RUSAGE_SELF, &usage);
            end=usage.ru_stime;
            double startTime=((double)start.tv_sec + (double)start.tv_usec)/10000;
            double endTime=((double)end.tv_sec+(double)end.tv_usec)/10000;
            tempTotal+=(endTime-startTime);
    }
    printf("Avg time taken: %f\n", tempTotal/iterations);

    printf("\n\n");
    double tempTotal2=0;
    for (int i=0; i<iterations; i++){

            struct rusage usage2;
            struct timeval start2, end2;

            getrusage(RUSAGE_SELF, &usage2);
            start2=usage2.ru_stime;

            for (int j=0; j<100000; j++) strcmp(str1, str3);

            getrusage(RUSAGE_SELF, &usage2);
            end2=usage2.ru_stime;

            double startTime2=((double)start2.tv_sec+(double)start2.tv_usec)/10000;
            double endTime2=((double)end2.tv_sec+(double)end2.tv_usec)/10000;
            tempTotal2+=endTime2-startTime2;
    }

    printf("Avg time taken: %f\n", tempTotal2/iterations);
    return 0;

}

【问题讨论】:

  • 但是,不相等的字符串总是需要更短的时间来比较相等的字符串,这并不正确——除非您只对相同长度的字符串感兴趣。
  • 请注意,(double)end2.tv_usec)/10000 应该是 (double)end2.tv_usec)/1000000(至少在 4 个不同的地方)。
  • 如果您的计时器不够准确,当然,您可以随时增加迭代次数。我通常要求实验至少需要 30 秒(如果不是更多),这通常需要数千万或数亿次迭代,甚至更多。 (如果迭代次数超过适合 32 位整数的次数,则必须使用两个嵌套循环并不少见。)计算机 快速
  • @Steve Summit 是的,对不起,我的意思是说我只研究相同长度的字符串。我会尝试实施您的其他建议,谢谢。

标签: c string optimization strcmp


【解决方案1】:

您的方案需要考虑以下几点:

  • 合理的编译器会识别出您的 strcmp 结果是 未使用,可以安全地完全消除呼叫
  • 一个合理的 编译器将认识到比较是循环不变的 (意味着它不会随着循环的迭代而改变)并且会 在循环外“提升”调用并执行一次,然后消除 完全循环,因为它什么都不做

解决这个问题的最简单方法是将 strcmp 包装到外部函数中,并将函数的定义放在不同的文件中,这样编译器就不能做任何有趣的事情(假设你不做跨文件优化)。我会做类似的事情:

for (int j=0; j<100000; j++) {
  external_strcmp(str1, str3);
}

然后放入不同的文件:

int external_strcmp(const char* str1, const char* str2) {
  return strcmp(str1, str2);
}

接下来我要做的就是让字符串 WAAAAYYYYY 更长,并增加你执行的迭代次数。就目前而言,您可能会看到 getrusage() 的开销使 strcmp 时间相形见绌。

祝你好运。性能分析是一个非常酷的领域。

【讨论】:

  • 谢谢你,但不幸的是它没有用。我正在创建一个边信道定时攻击来取乐。换句话说,我的项目采用长度为 40 的十六进制字符串,并一次强制一个字节。对于第一个字节,它检查 strcmp 对等长测试字符串的计时。一个循环遍历该测试字符串第一个字节上的所有十六进制字符,以检查哪个字符使 strcmp 运行最慢;该 char 被选为该位置的正确字节。对剩余的字节重复,直到两个字符串相等。还有其他建议可以使时间更准确吗?
  • 我建议查看编译器生成的代码。如果您包含了 strings.h,它应该内联 strcmp 的代码,您可以看到正在生成哪些指令。您可能会发现的一件事是,当您的字符串非常短(例如 16 字节或更少)或非常长(例如超过 256 字节)时,您的计时可能会发生有趣的事情,因此您可能希望使用超过字符串的内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多