質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

3593閲覧

関数は全て関数オブジェクトで実装するべき?

asobinin

総合スコア69

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2019/09/22 02:00

編集2019/09/22 02:07

以下のコードは、アッカーマン関数を用いてそれぞれの関数の実行速度を調べるものです。
時間を変えて何度か実行した結果が下の画像になります。単位はミリ秒です。
イメージ説明
すると、関数オブジェクト(コールバック)が関数よりも速いという結果が出ました。
関数オブジェクトのほうが速いというのであれば、実装する全ての関数を関数オブジェクトとして実装したほうが良いのでしょうか?

また関数の戻り値を型推論にして何度か実行してみたところ、実行時間が若干増えてしまいました。(何度やっても平均時間が型推論でないときを下回ることはなかった)
型推論はコンパイル時で完結するものだと思っているのですが、それは間違いなのでしょうか?それともただ単に実行タイミングが悪かったのでしょうか?
ついでに教えていただけたら幸いです。

Cpp

1#include <iostream> 2#include <chrono> 3using namespace std; 4 5// アッカーマン関数 6int acker(int x, int y) { 7 if (x == 0) return y + 1; 8 if (y == 0) return acker(x - 1, 1); 9 return acker(x - 1, acker(x, y - 1)); 10} 11 12// アッカーマン関数オブジェクト 13struct ackerman { 14 /*int operator()(int x, int y) { 15 if (x == 0) return y + 1; 16 if (y == 0) return ackerman()(x - 1, 1); 17 return ackerman()(x - 1, ackerman()(x, y - 1)); 18 }*/ 19 20 // callback 21 int operator()(ackerman& ack, int x, int y) { 22 if (x == 0) return y + 1; 23 if (y == 0) return ack(ack, x - 1, 1); 24 return ack(ack, x - 1, ack(ack, x, y - 1)); 25 } 26}; 27 28int main() 29{ 30 constexpr int N = 10; 31 constexpr int TIMES = 10; 32 constexpr int TIMES_TIMES = 10; 33 double ave = 0; 34 double aveave = 0; 35 ackerman ack; 36 int a[N]; 37 38 for (int l = 0; l < TIMES_TIMES; l++) { 39 for (int k = 0; k < TIMES; k++) { 40 auto start = chrono::system_clock::now(); 41 42 for (int i = 0; i < N; i++) { 43 //a[i] = acker(3, i); 44 //a[i] = ack(3, i); 45 //a[i] = ack(ack, 3, i); 46 } 47 48 auto end = chrono::system_clock::now(); 49 double time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); 50 51 //cout << time << "[ms]" << endl; 52 ave += time; 53 } 54 cout << "ave=" << (ave / N) << "[ms]" << endl; 55 aveave += ave; 56 ave = 0; 57 } 58 cout << "aveave=" << (aveave / TIMES_TIMES * TIMES) << "[ms]" << endl; 59 60 return 0; 61}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

現代のコンパイラは複雑です。 様々な要素の組み合わせによって上手く最適化可能なパターンになることもならないこともあります。 どのようにコンパイルされるか事前に予想するのはよほどそのコンパイラに通じた達人でもなければ出来ないものと思ってください。 そして最適化技術は激しく変遷しているので同じコンパイラでもほんの少しバージョンが変われば結果が違うということはあります。

また、個別の部分でわずかに速かったとしても全体としては観測不能なほどの差だったり逆効果だったりするのはよくあることです。 わずかな速度のために奇妙な設計をしてしまうと後に全体を見通して最適化しようとしても手を出しづらくなってしまいます。 「早すぎる最適化は諸悪の根源」というのはプログラミング業界ではよく知られている格言です。

まずは綺麗に設計して、速度が足りないようならどこが遅いのか測定して改良するという手順が望ましいですし、仮に関数オブジェクトが速いのだとしても「それは速い必要あるの? (そのために回りくどいデザインにしてでも?)」ということも配慮すべきです。

投稿2019/09/22 03:18

SaitoAtsushi

総合スコア5444

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asobinin

2019/09/22 12:20

シンプルな質問かと思いましたが、その解答は一筋縄ではいかないようですね。 メタプログラミングもそうですが、あまり速度に拘りすぎるのもよくないということですね。 C++ではそれらの行為が全て許されてしまうので、速度と見た目の折り合いは非常に難しいです。
guest

0

こんにちは。

まず、関数と関数オブジェクト(非コールバック)の差が激しいことが非常に気になります。
同じ処理を事実上同じコードで処理しているので結果がこんなに差がでることが不思議です。

ちょっとやってみたのですが、最適化ON/OFFで結果が変わります。
そして、最適化ONの場合、関数バージョンは実行時間が0mSecになります。ほぼ全てインライン展開されてしまう場合があるということのようです。(xをコンパイル時定数であたえていますし)
インライン展開を無限に行うことはできないので「どこまで」展開するかコンパイラがよしなに決めていると思いますが、その相違がありそうです。最適化OFFでも最適化を全くしないわけではないですから、似たような振る舞いはありえます。
(実際にどうなっているのか把握するためにはy_waiwaiさんも言っているようにアセンブラ・コードを見るのが確実です。アセンブラの勉強から始める場合はかなりたいへんですが、長いプログラマー人生の中で1度はやってみてもよいと思います。)

もし、実使用時の高速化を目的に速度比較されているのでしたら、コンパイラの実力がでない最適化OFFではなく、実力が発揮される最適化ONで比較することをお勧めします。

投稿2019/09/22 03:50

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

この質問の回答ではないですが、

そういうことを調べるのなら、まずはアセンブルリストを出してどういう命令コードが生成されるのかを調べましょう。
それぞれの場合で、なにか変わることをリストアップしていけばとりあえずその傾向が出てくると思います。

で、これだけで済めばいいのですが、イマドキのCPUではキャッシュメモリというのが搭載されてます。また、命令の実行に際してはパイプライン処理というのがなされます。
そのどれもが実行時間というのに深く関わるものなので、そこらへんが実際にはどう動くのかの理屈をすべて理解していかないと、なにが早くてなにが遅いということを判断するのはなかなか難しいと思います。

がんばってください。

投稿2019/09/22 02:34

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asobinin

2019/09/22 02:48

根本的な原因を特定するにはやはり、アセンブラや機械語など低レベルな部分を調べないといけないのですね・・・ がんばります
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問