8

プロファイリングの問題があります - 次のコードがあると想像してください...

void main()
{
    well_written_function();
    badly_written_function();
}
void well_written_function()
{
    for (a small number)
    {
        highly_optimised_subroutine();
    }
}
void badly_written_function()
{
    for (a wastefully and unnecessarily large number)
    {
        highly_optimised_subroutine();
    }
}
void highly_optimised_subroutine()
{
    // lots of code
}

これを vtune (または他のプロファイラー) で実行すると、何か問題があることに気付くのは非常に困難です。すべてのホットスポットは、既に最適化されている「// 多くのコード」とマークされたセクションに表示されます。badly_written_function() は、すべての問題の原因であるにもかかわらず、強調表示されません。

問題を見つけるのに役立つ vtune の機能はありますか?

badly_written_function()とそのすべてのサブ関数にかかった時間を見つけることができるモードはありますか?

4

4 に答える 4

1
于 2010-06-16T14:20:48.800 に答える
1

これは通常、「コールグラフ プロファイル」として知られていますが、Visual Studio がそれを行うと確信しています。

于 2010-06-16T10:51:30.413 に答える
1

独自の非常に単純なプロファイラーを作成することは、それほど難しくありません。main() に挿入します。

int main()
{
    profileCpuUsage(1);                 // start timer #1
    well_written_function();
    profileCpuUsage(2);                 // stop timer #1, and start timer #2
    badly_written_function();
    profileCpuUsage(-1);                // print stats for timers #1 and #2
    return 0;
}

どこ:

#define NUMBER(a) ((int)(sizeof(a) / sizeof(a)[0]))

void profileCpuUsage(int slice)
{
    static struct {
        int iterations;
        double elapsedTime;
    } slices[30];                             // 0 is a don't care slice

    if (slice < 0) {                          // -1 = print
        if (slices[0].iterations)
            for (slice = 1; slice < NUMBER(slices); slice++)
                printf("Slice %2d  Iterations %7d  Seconds %7.3f\n", slice,
                    slices[slice].iterations, slices[slice].elapsedTime);
    }
    else {
        static int i;                         // = previous slice
        static double t;                      // = previous t1
        const double t1 = realElapsedTime();  // see below for definition
        assert (slice < NUMBER(slices));
        slices[i].iterations  += 1;
        slices[i].elapsedTime += t1 - t;      // i = 0 first time through
        i = slice;
        t = t1;
    }
}

確かに、この profileCpuUsage() を使用した簡単な例では、あまりメリットがありません。また、適切な場所で profileCpuUsage() を呼び出してコードを手動で計測する必要があるという欠点もあります。

ただし、次のような利点があります。

  • プロシージャだけでなく、任意のコード フラグメントの時間を計ることができます。
  • バイナリ検索を実行してコードのホットスポットを見つけたり削除したりするため、追加や削除がすばやくできます。
  • 関心のあるコードのみに焦点を当てています。
  • ポータブル!
  • 接吻

移植性のないトリッキーなことの 1 つは、関数 realElapsedTime() を定義して、有効な時間を取得するのに十分な粒度を提供することです。これは通常、私にとってはうまくいきます(CYGWINでWindows APIを使用):

#include <windows.h>
double realElapsedTime(void)   // <-- granularity about 50 microsec on test machines
{
    static LARGE_INTEGER freq, start;
    LARGE_INTEGER count;
    if (!QueryPerformanceCounter(&count))
        assert(0 && "QueryPerformanceCounter");
    if (!freq.QuadPart) {      // one time initialization
        if (!QueryPerformanceFrequency(&freq))
            assert(0 && "QueryPerformanceFrequency");
        start = count;
    }
    return (double)(count.QuadPart - start.QuadPart) / freq.QuadPart;
}

ストレートな Unix の場合、次の共通点があります。

double realElapsedTime(void)                      // returns 0 first time called
{
    static struct timeval t0;
    struct timeval tv;
    gettimeofday(&tv, 0);
    if (!t0.tv_sec)
        t0 = tv;
    return tv.tv_sec - t0.tv_sec + (tv.tv_usec - t0.tv_usec) / 1000000.;
}

realElapsedTime() は、プロセス時間ではなく実時間で表示されます。これは通常、私が求めているものです。

RDTSC を使用してより細かい粒度を実現するための移植性の低い方法もあります。たとえばhttp://en.wikipedia.org/wiki/Time_Stamp_Counterとそのリンクを参照してください。ただし、これらは試していません。

編集: ravenspointの非常に素晴らしい答えは、私のものとあまり似ていないようです。そして彼の答えは、私がしばしば不満を感じていた醜い数字ではなく、わかりやすい文字列を使用しています。しかし、これは約 12 行追加するだけで修正できます (ただし、これにより行数はほぼ2 倍になります!)。

malloc() の使用を避けたいことに注意してください。また、私は strcmp() についても少し懐疑的です。したがって、スライスの数が増えることはありません。また、ハッシュの衝突は、解決されるのではなく、単純にフラグが立てられます。人間のプロファイラーは、手動でスライスの数を 30 から増やすか、説明を変更することで、これを修正できます。未テスト

static unsigned gethash(const char *str)    // "djb2", for example 
{
    unsigned c, hash = 5381;
    while ((c = *str++))
        hash = ((hash << 5) + hash) + c;    // hash * 33 + c 
    return hash;
}

void profileCpuUsage(const char *description)
{
    static struct {
        int iterations;
        double elapsedTime;
        char description[20];               // added!
    } slices[30];

    if (!description) {
        // print stats, but using description, mostly unchanged...
    }
    else {
        const int slice = gethash(description) % NUMBER(slices);
        if (!slices[slice].description[0]) { // if new slice
            assert(strlen(description) < sizeof slices[slice].description);
            strcpy(slices[slice].description, description);
        }
        else if (!!strcmp(slices[slice].description, description)) {
            strcpy(slices[slice].description, "!!hash conflict!!");
        }
        // remainder unchanged...
    }
}

もう 1 つのポイントは、通常、リリース バージョンではこのプロファイリングを無効にすることです。これはravenspointの答えにも当てはまります。これは、邪悪なマクロを使用してそれを定義するトリックによって実行できます。

#define profileCpuUsage(foo)                // = nothing

これが行われた場合、もちろん、定義に括弧を追加して、無効化マクロを無効にする必要があります。

void (profileCpuUsage)(const char *description)...
于 2010-06-16T07:46:25.390 に答える
0

一般に、これは、呼び出された関数の時間を含む時間を確認するために、自己時間ではなく関数の合計時間を観察したいものです。

VTuneでは、そのためにトップダウンタブを使用することをお勧めします。または、さらに良い方法で、最新のアップデートを使用している場合は、新しい実験的なCaller-Calleeビューを試してください。詳細については、http://software.intel.com/en-us/forums/topic/376210を参照してください。それはあなたがあなたのプログラムで最も時間のかかるサブツリーが何であるかを見ることができるようにそれらの合計時間で関数のフラットリストを取得します。

于 2013-03-08T21:26:03.597 に答える