C++ pimpl成语浪费了一个指令与C风格?

Rob*_*b N 6 c++ optimization pimpl-idiom

(是的,我知道一台机器指令通常无关紧要.我问这个问题是因为我想了解pimpl习语,并以最好的方式使用它;而且因为有时我会关心一台机器指令.)

在下面的示例代码中,有两个类,ThingOtherThing.用户将包括"thing.hh". Thing使用pimpl成语来隐藏它的实现. OtherThing使用C样式 - 返回并获取指针的非成员函数.这种风格产生稍好的机器代码.我想知道:有没有办法使用C++风格 - 即,使函数成为成员函数 - 但仍然保存机器指令.我喜欢这种风格,因为它不会污染类外的命名空间.

注意:我只关注调用成员函数(在本例中calc).我不是在看对象分配.

下面是我的Mac上的文件,命令和机器代码.

thing.hh:

class ThingImpl;
class Thing
{
    ThingImpl *impl;
public:
    Thing();
    int calc();
};

class OtherThing;    
OtherThing *make_other();
int calc(OtherThing *);
Run Code Online (Sandbox Code Playgroud)

thing.cc:

#include "thing.hh"

struct ThingImpl
{
    int x;
};

Thing::Thing()
{
    impl = new ThingImpl;
    impl->x = 5;
}

int Thing::calc()
{
    return impl->x + 1;
}

struct OtherThing
{
    int x;
};

OtherThing *make_other()
{
    OtherThing *t = new OtherThing;
    t->x = 5;
}

int calc(OtherThing *t)
{
    return t->x + 1;
}
Run Code Online (Sandbox Code Playgroud)

main.cc(只是为了测试代码实际工作......)

#include "thing.hh"
#include <cstdio>

int main()
{
    Thing *t = new Thing;
    printf("calc: %d\n", t->calc());

    OtherThing *t2 = make_other();
    printf("calc: %d\n", calc(t2));
}
Run Code Online (Sandbox Code Playgroud)

Makefile文件:

all: main

thing.o : thing.cc thing.hh
    g++ -fomit-frame-pointer -O2 -c thing.cc

main.o : main.cc thing.hh
    g++ -fomit-frame-pointer -O2 -c main.cc

main: main.o thing.o
    g++ -O2 -o $@ $^

clean: 
    rm *.o
    rm main
Run Code Online (Sandbox Code Playgroud)

运行make然后查看机器代码.在Mac上我使用otool -tv thing.o | c++filt.在linux上我认为是objdump -d thing.o.以下是相关输出:

Thing :: calc():
0000000000000000 movq(%rdi),%r​​ax
0000000000000003 movl(%rax),%eax
0000000000000005 incl%eax
0000000000000007 ret
calc(OtherThing*):
0000000000000010 movl(%rdi),%eax
0000000000000012 incl%eax
0000000000000014 ret

由于指针间接,请注意额外的指令.第一个函数查找两个字段(impl,然后是x),而第二个函数只需要获取x.可以做些什么?

Mar*_*tos 7

一条指令很少花费很多时间来担心.首先,编译器可以将pImpl缓存在更复杂的用例中,从而在实际场景中分摊成本.其次,流水线架构使得几乎不可能预测时钟周期中的实际成本.如果您在循环中运行这些操作并计算差异,您将获得更加真实的成本概念.

  • 一些仔细的编码和很长的循环可能会出现一些差异,但你的结果并不让我感到惊讶. (2认同)

MSa*_*ers 5

不太难,只需在课堂上使用相同的技巧.任何中途不错的优化器都会内置琐碎的包装器.

class ThingImpl;
class Thing
{
    ThingImpl *impl;
    static int calc(ThingImpl*);
public:
    Thing();
    int calc() { calc(impl); }
};
Run Code Online (Sandbox Code Playgroud)