10
void do_something() {....}

struct dummy
{
   //even I dont call this, compiler will call it fall me, they need it
   void call_do_something() { this->do_something_member(); } 
   void do_something() {....}
};

私が知っていることによると、C ++のすべてのクラスまたは構造体は、データメンバーまたはクラスのメンバー関数にアクセスするときに、このポインターを暗黙的に呼び出します。これにより、C ++のパフォーマンスが低下しますか?

私が言いたいのは

int main()
{
  do_something(); //don't need this pointer
  dummy().call_do_something(); //assume the inline is prefect

  return 0;
}

call_do_somethingは、メンバー関数を呼び出すためにthisポインターを必要としますが、do_somethingのようなCはこのポインターを必要としません。このポインターは、Cのような関数と比較した場合にパフォーマンスの低下をもたらしますか?

マイクロ最適化を行う意味はありません。時間がかかるためですが、常に良い結果が得られるとは限りません。常に「測定する、考えない」というルールに従います。このポインタが好奇心のためにパフォーマンスの低下をもたらすかどうかを知りたいです。

4

2 に答える 2

8

状況にもよりますが、通常、最適化を有効にしていれば、C バージョンよりも高価になることはありません。他の機能に本当に「お金を払う」のthisは、継承と仮想関数を使用するときだけです。それ以外は、コンパイラは十分にスマートなので、this使用していない関数で時間を無駄にすることはありません。次の点を考慮してください。

#include <iostream>

void globalDoStuff()
{
    std::cout << "Hello world!\n";
}

struct Dummy
{
    void doStuff() { callGlobalDoStuff(); }
    void callGlobalDoStuff() { globalDoStuff(); }
};

int main()
{
    globalDoStuff();

    Dummy d;
    d.doStuff();
}

GCC 最適化レベルO3でコンパイルすると、次の逆アセンブルが得られます (余分なジャンクをカットして を表示するだけですmain())。

_main:
0000000100000dd0    pushq   %rbp
0000000100000dd1    movq    %rsp,%rbp
0000000100000dd4    pushq   %r14
0000000100000dd6    pushq   %rbx
0000000100000dd7    movq    0x0000025a(%rip),%rbx
0000000100000dde    leaq    0x000000d1(%rip),%r14
0000000100000de5    movq    %rbx,%rdi
0000000100000de8    movq    %r14,%rsi
0000000100000deb    callq   0x100000e62 # bypasses globalDoStuff() and just prints "Hello world!\n"
0000000100000df0    movq    %rbx,%rdi
0000000100000df3    movq    %r14,%rsi
0000000100000df6    callq   0x100000e62 # bypasses globalDoStuff() and just prints "Hello world!\n"
0000000100000dfb    xorl    %eax,%eax
0000000100000dfd    popq    %rbx
0000000100000dfe    popq    %r14
0000000100000e00    popq    %rbp
0000000100000e01    ret

Dummyとの両方を完全に最適化globalDoStuff()し、 の本体に置き換えただけであることに注意してくださいglobalDoStuff()globalDoStuff()呼び出されることもありませんし、Dummy構築されることもありません。代わりに、コンパイラ/オプティマイザはそのコードを 2 つのシステム コールに置き換えて、"Hello world!\n"直接出力します。教訓は、コンパイラとオプティマイザは非常にスマートであり、一般的に、必要のないものにはお金を払わないということです。

一方、 のメンバー変数を操作するメンバー関数があるとしますDummy。これには C 関数に比べてペナルティがあると思うかもしれませんね。C 関数は変更するオブジェクトへのポインターを必要とするため、おそらくそうではありませんthis

したがって、一般に、C++ では C と比較して追加料金を支払うことはありませんthis。仮想関数には、呼び出す適切な関数を検索する必要があるため、(小さな) ペナルティがある可能性がありますが、ここで検討しているケースはそうではありません。

コンパイラで最適化をオンにしない場合、確かにペナルティが発生する可能性がありますが、なぜ最適化されていないコードを比較するのでしょうか?

于 2012-11-30T06:17:15.390 に答える
3
#include <iostream>
#include <stdint.h>
#include <limits.h>
struct Dummy {
  uint32_t counter;
  Dummy(): counter(0) {}
  void do_something() {
    counter++;
  }
};

uint32_t counter = 0;

void do_something() { counter++; }

int main(int argc, char **argv) {
  Dummy dummy;
  if (argc == 1) {
    for (int i = 0; i < INT_MAX - 1; i++) {
      for (int j = 0; j < 1; j++) {
        do_something();
      }   
    }   
  } else {
    for (int i = 0; i < INT_MAX - 1; i++) {
      for (int j = 0; j < 1; j++) {
        dummy.do_something();
      }   
    }   
    counter = dummy.counter;
  }
  std::cout << counter << std::endl;
  return 0;
}

gcc バージョン 4.3.5 (Debian 4.3.5-4)、64 ビット、フラグなしで平均 10 回実行:

グローバルカウンターあり: 0m15.062s

ダミーオブジェクトあり: 0m21.259s

Lythが提案したように、次のようにコードを変更すると:

#include <iostream>
#include <stdint.h>
#include <limits.h>

uint32_t counter = 0;

struct Dummy {
  void do_something() {
    counter++;
  }
};


void do_something() { counter++; }

int main(int argc, char **argv) {
  Dummy dummy;
  if (argc == 1) {
    for (int i = 0; i < INT_MAX; i++) {
        do_something();
    }   
  } else {
    for (int i = 0; i < INT_MAX; i++) {
        dummy.do_something();
    }   
  }
  std::cout << counter << std::endl;
  return 0;
}

すると不思議なことに、

グローバルカウンターあり: 0m12.062s

ダミーオブジェクトあり: 0m11.860s

于 2012-11-30T06:37:27.097 に答える