8

次のプログラムはfun を2 ^ (MAXD + 1) 回呼び出します。ただし、最大再帰深度が MAXD を超えることはありません (私の考えが正しければ)。したがって、コンパイルに時間がかかる場合がありますが、RAM を消費することはありません。

#include<iostream>

const int MAXD = 20;

constexpr int fun(int x, int depth=0){
  return depth == MAXD ? x : fun(fun(x + 1, depth + 1) + 1, depth + 1);
}

int main(){
  constexpr int i = fun(1);
  std::cout << i << std::endl;
}

問題は、RAM を消費することがまさにそれであるということです。MAXD を 30 まで上げると、GCC 4.7.2 が 3 GB 程度をすばやく割り当てた後、ラップトップがスワップし始めます。現在、clang 3.1 にアクセスできないため、clang 3.1 ではまだ試していません。

私の唯一の推測は、これは GCC が巧妙になりすぎて、テンプレートの場合のように関数呼び出しをメモ化しようとすることに関係があるということです。だとすると、MRU キャッシュ テーブルのサイズとか、メモ化の量に制限がないのはおかしいと思いませんか? 無効にするスイッチが見つかりませんでした。

なぜ私はこれをするのですか?私は、遺伝的プログラミングなどの高度なコンパイル時ライブラリを作成するというアイデアをいじっています。コンパイラにはコンパイル時の末尾呼び出しの最適化がないため、ループするものすべてに再帰が必要になるのではないかと心配しています (最大再帰深度パラメータを上げても、要求するのが少し醜いように見えます) すべての RAM がすぐに割り当てられていっぱいになります無意味なスタックフレームでそれ。したがって、深いスタックなしで任意に多くの関数呼び出しを取得するための上記のソリューションを思いつきました。このような機能は、折りたたみ/ループまたはトランポリンに使用できます。

編集:clang 3.1で試してみましたが、どれだけ長く機能させても(つまり、MAXDをどれだけ高くしても)メモリリークはまったくありません。予想通り、CPU 使用率はほぼ 100%、メモリ使用率はほぼ 0% です。おそらく、これは GCC の単なるバグです。

4

2 に答える 2

2

これは constexpr に関する決定的なドキュメントではないかもしれませんが、gcc constexpr wiki からリンクされている主要なドキュメントです。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf

...そしてそれは言う...

定数式では、すべての形式で再帰を (まだ) 禁止しています。定数式の評価における再帰の深さの実装制限により、コンパイラが永久に再帰する可能性を回避できるため、これは厳密には必要ありません。ただし、再帰の説得力のある使用例が見られるまで、再帰を許可することは提案しません。

したがって、言語の境界と、gcc が constexpr を実装するために選択した方法にぶつかっていると思います (関数全体をインラインで生成しようとして、それを評価/実行しようとしている可能性があります)。

于 2012-11-16T09:30:08.580 に答える
1

あなたの答えは、「関数ランタイムを実行し、それを長時間実行できることを観察することによって」コメントにあります。これは、fun(x + 1、depth + 1)への最も再帰的な呼び出しによって引き起こされます。

constexprを削除してコンパイル時関数ではなくランタイム関数に変更し、長時間実行されたことを確認した場合、これは非常に深く繰り返されていることを示しています。

関数がコンパイラーによって実行されるとき、関数は深く再帰する必要がありますが、実際にはマシンコードを生成して実行していないため、再帰にスタックを使用しません。

于 2012-11-15T16:39:20.513 に答える