jjzjj

c++ - Clang 为直觉上应该等效的表达式提供非常不同的性能

coder 2024-02-21 原文

有人可以向我解释这些表达式之间的这些显着性能差异,我希望它们能提供相似的性能。我在 Release模式下使用 Apple LLVM 版本 5.1 (clang-503.0.38)(基于 LLVM 3.4svn)进行编译。

这是我的测试代码(只需将 CASE 更改为 1、2、3 或 4 来测试自己):

#include <iostream>
#include <chrono>

#define CASE 1

inline int foo(int n) {
    return
#if CASE == 1
    (n % 2) ? 9 : 6

#elif CASE == 2
    (n % 2) == true ? 9 : 6

#elif CASE == 3
    6 + (n % 2) * 3

#elif CASE == 4
    6 + bool(n % 2) * 3

#endif
    ;
}

int main(int argc, const char* argv[])
{
    std::chrono::time_point<std::chrono::system_clock> start, end;
    start = std::chrono::system_clock::now();

    int n = argc;
    for (int i = 0; i < 100000000; ++i) {
        n += foo(n);
    }

    end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;

    std::cout << "elapsed time: " << elapsed_seconds.count() << "\n";
    std::cout << "value: " << n << "\n";

    return 0;
}

这是我得到的时间:

CASE   EXPRESSION                TIME
1      (n % 2) ? 9 : 6           0.1585
2      (n % 2) == true ? 9 : 6   0.3491
3      6 + (n % 2) * 3           0.2559
4      6 + bool(n % 2) * 3       0.1906

下面是 CASE 1 和 CASE 2 在汇编上的区别:

案例 1:

Ltmp12:
LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    ##DEBUG_VALUE: main:argv <- RSI
    ##DEBUG_VALUE: i <- 0
    .loc    1 24 0                  ## /Test/main.cpp:24:0
    movl    %ebx, %ecx
    andl    $1, %ecx
    leal    (%rcx,%rcx,2), %ecx
Ltmp13:
    .loc    1 48 14                 ## /Test/main.cpp:48:14
    leal    6(%rbx,%rcx), %ebx

案例 2:

Ltmp12:
LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    ##DEBUG_VALUE: main:argv <- RSI
    ##DEBUG_VALUE: i <- 0
    .loc    1 24 0                  ## /Test/main.cpp:24:0
    movl    %ebx, %ecx
    shrl    $31, %ecx
    addl    %ebx, %ecx
    andl    $-2, %ecx
    movl    %ebx, %edx
    subl    %ecx, %edx
    cmpl    $1, %edx
    sete    %cl
    movzbl  %cl, %ecx
    leal    (%rcx,%rcx,2), %ecx
Ltmp13:
    .loc    1 48 14                 ## /Test/main.cpp:48:14
    leal    6(%rbx,%rcx), %ebx

下面是 CASE 3 和 CASE 4 在汇编上的区别:

案例 3:

Ltmp12:
LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    ##DEBUG_VALUE: main:argv <- RSI
    ##DEBUG_VALUE: i <- 0
    .loc    1 24 0                  ## /Test/main.cpp:24:0
    movl    %ebx, %ecx
    shrl    $31, %ecx
    addl    %ebx, %ecx
    andl    $-2, %ecx
    movl    %ebx, %edx
    subl    %ecx, %edx
    leal    (%rdx,%rdx,2), %ecx
Ltmp13:
    .loc    1 48 14                 ## /Test/main.cpp:48:14
    leal    6(%rbx,%rcx), %ebx

案例 4:

Ltmp12:
LBB0_1:                                 ## =>This Inner Loop Header: Depth=1
    ##DEBUG_VALUE: main:argv <- RSI
    ##DEBUG_VALUE: i <- 0
    .loc    1 24 0                  ## /Test/main.cpp:24:0
    movl    %ebx, %ecx
    andl    $1, %ecx
    negl    %ecx
    andl    $3, %ecx
Ltmp13:
    .loc    1 48 14                 ## /Test/main.cpp:48:14
    leal    6(%rbx,%rcx), %ebx

最佳答案

这个答案目前只涵盖了前两种情况的区别。


(n % 2) 的可能值是多少?当然,它是 01,对吧?

错了。它是 01 -1。因为 n 是一个有符号整数,并且 the result of % can be negative .

(n % 2) ? 6 : 9 将表达式 n % 2 隐式转换为 bool。此转换的结果是 true IFF 值非零。因此,转换等同于 (n % 2) != 0

(n % 2) == true 中? 6 : 9,对于比较 (n % 2) == true通常的算术转换应用于两侧(注意 bool 是算术类型)。 true 被提升为值 1int。所以转换等同于 (n % 2) == 1

这两个转换 (n % 2) != 0(n % 2) == 1 对于负值 n 产生不同的结果:令n = -1。那么n % 2 == -1,并且-1 != 0true,但是-1 == 1false

因此,编译器必须引入一些额外的复杂性来处理符号。

如果您将 n 设为无符号整数,或以任何其他方式消除符号问题(例如通过比较 n % 2 !=假)。


我是通过查看程序集输出得到这个想法的,尤其是下面一行:

shrl    $31, %eax

一开始使用最高位对我来说毫无意义,直到我意识到最高位被用作符号。

关于c++ - Clang 为直觉上应该等效的表达式提供非常不同的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22819232/

有关c++ - Clang 为直觉上应该等效的表达式提供非常不同的性能的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  5. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. ruby - 正则表达式将非英文字母匹配为非单词字符 - 2

    @raw_array[i]=~/[\W]/非常简单的正则表达式。当我用一些非拉丁字母(具体来说是俄语)尝试时,条件是错误的。我能用它做什么? 最佳答案 @raw_array[i]=~/[\p{L}]/使用西里尔字符进行测试。引用:http://www.regular-expressions.info/unicode.html#prop 关于ruby-正则表达式将非英文字母匹配为非单词字符,我们在StackOverflow上找到一个类似的问题: https://

  8. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  9. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  10. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

随机推荐