質問
プロファイリングを行うためにCでスコープガードを使用したいと考えています。
ある行事にどれくらいの時間を費やしているのか知りたいです。私がやることは次のとおりです。
int function() {
tic();
... do stuff ...
if (something)
{
toc();
return 0;
}
toc();
return 1;
}
関数を終了するたびに toc ステートメントを配置する必要があります。どこにでも目次をコピー&ペーストすることなく、それを実行したいと考えています。マクロか何かを使用してそれを行う一般的な方法はありますか?また、プロファイリングする必要がある関数がたくさんあるため、関数の呼び出し方法を変更したくありません。
ありがとう
解決
この関数が呼び出される方法を変更しません。あなたは、すべての単一の機能をプロファイリングすることができるようにしたい場合は、おそらくあまり使用もののます。
static inline int real_function() {
// previous contents of function(), with no tic or toc
}
int function() {
tic();
int r = real_function();
toc();
return r;
}
誰もが言うように:プロファイラを使用し、それが長期的にはあなたに多くの労力を節約します。彼らは言っていないよう:お使いのプラットフォームが1つを持っている場合は、
。 そうでない場合は、、そして最も簡単な関数は一つだけの出口点を持っている必要があり、その出口点は、マクロ経由でなければならないこと(コーディングルールのように)言うことかもしれません。その後、入り口と出口で楽器にコードで、すべての機能を手動ですることができます。複数戻るとレガシー機能は、上記のように包まれることができる。
あなたのコンパイラ缶の混乱という、このような何かをやっている念頭に置いても、熊あなたがアップ。あなたがこれを書いかもしれません。
tic();
do_something();
int i = something_else();
toc();
return i;
コンパイラはsomething_elseがsomething_elseはかなりの時間を要するにもかかわらず、それはこのにコードを回すかもしれない、その後、何の副作用を持っていないと判断した場合:
tic();
do_something();
toc();
return something_else();
そして、あなたのプロファイルデータはあなたの関数に費やされた時間を過小見積もります。本物のプロファイラを持って良いので、それはだもう一つの理由 - それはコンパイラと協働することができます。
他のヒント
あなたが好きなマクロを定義することができます:
#define TOC_RETURN(x) \
do { \
toc(); \
return x; \
} while(0)
どこでも動作するはずです。あなたはそれを置きます。そして、あなたはreturn *;
でTOC_RETURN(*)
を交換し自動化することができます。
なぜ gprofをのように、実際のプロファイリングツールを使用していませんか?
「再定義する" マクロ経由で戻ります:(免責事項をご覧ください)
#include <stdio.h>
void tic() { printf("tic\n"); }
void toc() { printf("toc\n"; }
#define return toc(); return
int foo() {
tic();
return 0;
}
#undef return
int main() {
foo();
return 0;
}
免責事項: これは次の理由から、醜くて危険だと考えられます。
- を使用しない限り、void 関数では機能しません。 戻る;-ステートメント。
- MSVC8 では動作しますが、移植性や標準性がない可能性があります。
- キーワードを定義すべきではありません。
私はこのためにマクロをお勧めしません。あなたは一度だけしばらくのコードをプロファイリングし、ちょうどその目的のためにいくつかの特別なマクロで「リターン」を交換すると、コードが読みにくくなります。
それは次の?
としてやって良いとは限りませんtic();
call_function();
toc();
この自動的にハンドル機能から「すべての出口点」ます。
P.S。なぜあなたはプロファイラを使用していない?
本物のプロファイラだけでプロファイル機能を有効にしてコンパイルし、コードを変更するあなたを必要としません。
私は 非常に遅く ただし、GCC 拡張機能を使用して C でスコープ ガードを行う別の方法もあります cleanup
属性。の cleanup
属性は、変数がスコープ外になったときに実行される関数を変数宣言に付加します。本来は動的に割り当てられた型のメモリ割り当て解除を実行することを目的としていましたが、スコープ ガードとしても悪用される可能性があります。
void cleanup_toc(int *ignored __attribute__((__unused__))) { toc(); }
int function(void) {
tic();
int atexit __attribute__((__cleanup__(cleanup_toc))) = 0;
//... do stuff ...
if (something) {
return 0;
}
return 1;
}
このソリューションではマクロを使用しませんが、もちろんこれをマクロにラップすることもできます。例えば:
#define CONCATENATE_IMPL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_IMPL(x, y)
#define ATEXIT(f) int CONCATENATE(atexit, __LINE__) __attribute__((__cleanup__(f))) = 0
int function(void) {
ATEXIT(cleanup1); // These are executed in reverse order, i.e.
ATEXIT(cleanup2); // cleanup2 will run before cleanup1.
}
うーん、多分マクロ(マクロの家族、本当に)で関数呼び出しをラップ?ここでRETVALを引数を取らず、返すものです。
// define the wrapper for name
#define DEFTIMECALL0(Retval,name) \
Retval timed##name() \
{ \
Retval ret;
tic(); \
ret = name(); \
toc(); \
return ret; \
}
あなたはRETVALとvoidを返すバージョンで、あなたが作る関数呼び出しのすべてのアリティのためのマクロが必要です。
編集多分ラッパー関数を定義する際にも、ポイントがない、とだけ(各アリティおよび戻り値の型/無効のバージョンで、再び)マクロの家族を持って、より良いためにどのラップA callsites
で直接TIC / TOC内の関数呼び出しない、本質的にあなたのためにこれを行うプロファイラを、インストルメントを恐れてはいけます。