代码之家  ›  专栏  ›  技术社区  ›  Daniel Langr

即使保证了零值,gcc也会生成非零值测试。

  •  7
  • Daniel Langr  · 技术社区  · 6 年前

    请考虑以下主要为基准测试而创建的类:

    class String {
      char* data_; 
    public:
      String(const char* arg = "") : data_(new char[strlen(arg) + 1]) { strcpy(data_, arg); }
      String(const String& other) : String(other.data_) { }
      String(String&& other) noexcept : data_(other.data_) { other.data_ = nullptr; }
      String& operator=(String other) noexcept { swap(other); return *this; }  
      ~String() { delete[] data_; }
      void swap(String& rhs) noexcept { std::swap(data_, rhs.data_); }
      const char* data() const { return data_; }   
    };
    void swap(String& lhs, String& rhs) noexcept { lhs.swap(rhs); }
    

    我正在尝试将两个实例的交换效率与自定义 swap std::swap . 用于自定义 掉期 ,合同一般条件8.2( -O2 )生成以下x86_64程序集:

    mov     rax, QWORD PTR [rdi]
    mov     rdx, QWORD PTR [rsi]
    mov     QWORD PTR [rdi], rdx
    mov     QWORD PTR [rsi], rax
    ret
    

    与两个指针的交换完全匹配。然而,对于 STD::掉期 ,生成的程序集为:

      mov     rdx, QWORD PTR [rsi]
      mov     QWORD PTR [rsi], 0    // (A)
      mov     rax, QWORD PTR [rdi]
      mov     QWORD PTR [rdi], 0    // (1)
      mov     QWORD PTR [rsi], rax  // (B)
      mov     rax, QWORD PTR [rdi]  // (2)
      mov     QWORD PTR [rdi], rdx
      test    rax, rax              // (3)
      je      .L3
      mov     rdi, rax
      jmp     operator delete[](void*) 
    .L3:
      ret
    

    我好奇的是,gcc为什么会生成如此低效的代码。指令(1)设置 [rdi] 归零。然后将0加载到 rax (2)。然后, 拉克斯 是否测试(3) operator delete 应该被叫来。

    为什么GCC测试 拉克斯 如果保证为零?对于优化器来说,避免这个测试似乎是一个非常简单的例子。

    Godbolt演示: https://godbolt.org/z/WNm2if


    效率低下的另一个原因是0被写入 [rsi] 首先(a),然后用另一个值(b)覆盖它。

    底线:我希望编译器为 STD::掉期 以及风俗习惯 掉期 ,但这不会发生。这表明,即使对于支持移动语义的类,编写自定义交换函数也是有意义的。

    0 回复  |  直到 6 年前