【问题标题】:How to let MSVC compiler optimize multiple step POD initialization?如何让 MSVC 编译器优化多步 POD 初始化?
【发布时间】:2018-06-06 22:58:59
【问题描述】:

我已经制作了这个示例代码:

#include <vector>

struct POD {
    int a;
    int b;
    int c;

    inline static POD make_pod_with_default()
    {
        POD p{ 41, 51, 61 };
        return p;
    }

    inline void change_pod_a(POD &p, int a) {
        p.a = a;
    }

    inline void change_pod_b(POD &p, int b) {
        p.b = b;
    }

    static POD make_pod_with_a(int a) {
        POD p = make_pod_with_default();
        p.change_pod_a(p, a);
        return p;
    }

    static POD make_pod_with_b(int a) {
        POD p = make_pod_with_default();
        p.change_pod_b(p, a);
        return p;
    }
};

int main()
{
    std::vector<POD> vec{};
    vec.reserve(2);
    vec.push_back(POD::make_pod_with_a(71));
    vec.push_back(POD::make_pod_with_b(81));
    return vec[0].a + vec[0].b + vec[0].c + vec[1].a + vec[1].b + vec[1].c;
}

在编译后的汇编代码中,我们可以看到正在为第一个 vec.push_back(...) 调用生成以下指令:

...
mov      DWORD PTR $T2[esp+32], 41 ; 00000029H
...
mov      DWORD PTR $T2[esp+36], 51 ; 00000033H
...
mov      DWORD PTR $T5[esp+32], 71 ; 00000047H
...
mov      DWORD PTR $T6[esp+44], 61 ; 0000003dH
...

71 有一个到 [esp+32] 的 mov,但是 41 的到 [esp+32] 的 mov 仍然存在,没用!我怎样才能为 MSVC 编写能够实现这种优化的代码,MSVC 甚至能够做到吗?

GCC 和 CLANG 都提供了更优化的版本,但是 CLANG 以一种非常干净和合乎逻辑的方式在几乎没有开销的情况下大幅失败:

CLANG 生成代码:

main: # @main
push rax
mov edi, 24
call operator new(unsigned long)
mov rdi, rax
call operator delete(void*)
mov eax, 366
pop rcx
ret

一切都是在编译时完成的 71 + 51 + 61 + 41 + 81 + 61 = 366! 我必须承认,看到我的程序在编译时被计算并且仍然在程序集中对 vec.reserve() 的调用很痛苦……但是到目前为止,CLANG 仍然占了上风!加油 MSVC,这不是 volatile 的向量。

【问题讨论】:

  • 较短的复制者:POD make_pod() { POD p = POD{0, 1, 2}; p.c = 3; return p; }。在这种形式下,可以通过将POD p = POD{0, 1, 2}; 更改为POD p = {0, 1, 2}; 来优化MSVC。不确定当初始化程序是函数调用的结果时是否有类似的解决方法。
  • 我相信您的意思是“将 POD p = POD{0, 1, 2}; 更改为 POD p = {0, 1, 3};”。问题是,这个样本是我设计的精简版。我对多步初始化有可读性和设计原因:某些步骤是稍后才调用的,或者从未调用过。我在示例中包含了您建议的优化示例,因此每个人都可以分辨出我需要的优化。
  • 不,POD p = {0, 1, 3}; 不是我的意思。将其更改为 POD p = {0, 1, 2}; 并在它确实让 MSVC 优化它之后留下 p.c = 3; 分配。
  • 当然,如果实际程序中的第二步是有条件地调用,或者在其他中间函数调用之后调用,那么这种情况下这种优化很可能是不可能的。
  • @hvd 我发现了不同之处,所以对于 MSVC "A a = {...};"和“A a = A{}”不一样?

标签: c++ optimization visual-c++ c++17 clang++


【解决方案1】:

如果你把你的方法转为constexpr,你可能会这样做:

constexpr POD step_one()
{
    POD p{2, 5, 11};
    p.b = 3;
    return p;
}

constexpr void step_two(POD &p)
{
    p.c = 5;
}

constexpr POD make_pod(){
    POD p = step_one();
    step_two(p);
    return p;
}

POD make_pod_final()
{
    constexpr POD res = make_pod();
    return res;
}

导致:

make_pod_final PROC
    mov      eax, DWORD PTR $T1[esp-4]
    mov      DWORD PTR [eax], 2
    mov      DWORD PTR [eax+4], 3
    mov      DWORD PTR [eax+8], 5
    ret      0

Demo

【讨论】:

  • 这是一个很好的尝试,它很聪明,对我拥有的其他东西很有帮助,非常感谢你!但是我的 POD 是在巨大的容器中动态分配的 :( 这个认识真的很重要,我明天会编辑我的问题以更好地代表我的问题。
  • 我现在更清楚地知道我需要什么:我需要一个调用其他内联函数的函数来再次优化。类似于第二次优化。 MSVC 能做到吗?我正在安装带有 MSVC 集成的 CLANG 作为替代方案,但我宁愿继续使用 MSVC 编译器...
  • 你不会相信 CLANG 编译成...: "mov eax, 9 pop rcx ret" LMAO!
猜你喜欢
  • 2015-01-11
  • 2021-07-31
  • 2015-05-13
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 2018-07-29
相关资源
最近更新 更多