Linuxで実行されているC ++コードをプロファイリングするにはどうすればよいですか?

1891
Gabriel Isenberg 2008-12-18 10:29.

Linux上で実行されているC ++アプリケーションがあり、最適化を進めています。コードのどの領域の実行が遅いかを特定するにはどうすればよいですか?

19 answers

1459
Mike Dunlavey 2008-12-19 04:36.

プロファイラーを使用することが目標の場合は、提案されたものの1つを使用してください。

ただし、急いでいて、主観的に遅いときにデバッガーでプログラムを手動で中断できる場合は、パフォーマンスの問題を見つける簡単な方法があります。

数回停止し、そのたびにコールスタックを確認します。20%や50%など、一定の割合の時間を無駄にしているコードがある場合、それは各サンプルの行為でそれをキャッチする確率です。つまり、これはおおよそ、それが表示されるサンプルの割合です。知識に基づいた当て推量は必要ありません。問題が何であるかについて推測がある場合、これはそれを証明または反証します。

サイズの異なる複数のパフォーマンスの問題が発生する可能性があります。それらのいずれかをクリーンアップすると、残りのパスの割合が大きくなり、後続のパスで見つけやすくなります。この拡大効果は、複数の問題を組み合わせた場合、本当に大きなスピードアップ要因につながる可能性があります。

警告:プログラマーは、自分で使用したことがない限り、この手法に懐疑的である傾向があります。彼らはプロファイラーがあなたにこの情報を与えると言うでしょう、しかしそれは彼らがコールスタック全体をサンプリングしそしてあなたにサンプルのランダムなセットを調べさせた場合にのみ真実です。(要約は洞察が失われる場所です。)コールグラフは同じ情報を提供しません。なぜなら、

  1. それらは命令レベルで要約されません、そして
  2. それらは、再帰の存在下で紛らわしい要約を提供します。

彼らはまた、実際にはどのプログラムでも機能するのに、おもちゃのプログラムでのみ機能すると言うでしょう。また、見つけるのに問題が多い傾向があるため、より大きなプログラムでよりうまく機能するようです。問題ではないものが見つかることもあると言われますが、それは一度見た場合にのみ当てはまります。複数のサンプルで問題が発生した場合、それは現実のものです。

PSこれは、Javaの場合のように、ある時点でスレッドプールのコールスタックサンプルを収集する方法がある場合は、マルチスレッドプログラムでも実行できます。

PPS大まかな一般として、ソフトウェアに含まれる抽象化レイヤーが多いほど、それがパフォーマンスの問題(およびスピードアップの機会)の原因であることがわかる可能性が高くなります。

追加:明らかではないかもしれませんが、スタックサンプリング手法は再帰が存在する場合でも同様にうまく機能します。その理由は、命令を削除することで節約できる時間は、サンプル内で発生する可能性のある回数に関係なく、命令を含むサンプルの割合で概算されるためです。

私がよく耳にするもう1つの反対意見は、「ランダムな場所で停止し、実際の問題を見逃してしまう」というものです。これは、本当の問題が何であるかについての事前の概念を持っていることから来ています。パフォーマンスの問題の重要な特性は、期待に反することです。サンプリングは何かが問題であるとあなたに告げます、そしてあなたの最初の反応は不信です。それは当然のことですが、問題が見つかった場合はそれが現実であり、その逆も同様です。

追加:それがどのように機能するかについてベイズの説明をさせてください。Iコールスタック上にある命令(呼び出しまたはその他)fが、時間の一部であると仮定します(したがって、そのコストがかかります)。簡単にするために、何であるかわからないとしますfが、0.1、0.2、0.3、... 0.9、1.0のいずれかであり、これらの可能性のそれぞれの事前確率は0.1であるため、これらのコストはすべて同じように発生する可能性があります。アプリオリ。

次に、スタックサンプルを2つだけ取得Iし、両方のサンプルに命令が表示され、観測値が指定されているとしo=2/2ます。これは私たちに、周波数の新しい推計与えfのをIこれによると、:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.1    1     1             0.1          0.1            0.25974026
0.1    0.9   0.81          0.081        0.181          0.47012987
0.1    0.8   0.64          0.064        0.245          0.636363636
0.1    0.7   0.49          0.049        0.294          0.763636364
0.1    0.6   0.36          0.036        0.33           0.857142857
0.1    0.5   0.25          0.025        0.355          0.922077922
0.1    0.4   0.16          0.016        0.371          0.963636364
0.1    0.3   0.09          0.009        0.38           0.987012987
0.1    0.2   0.04          0.004        0.384          0.997402597
0.1    0.1   0.01          0.001        0.385          1

                  P(o=2/2) 0.385                

最後の列は、たとえば、f> = 0.5の確率は92%であり、以前の仮定の60%から増加していることを示しています。

以前の仮定が異なると仮定します。P(f=0.1).991(ほぼ確実)であり、他のすべての可能性はほとんど不可能(0.001)であると仮定します。言い換えれば、私たちの以前の確実性はそれIが安いということです。次に、次のようになります。

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.001  1    1              0.001        0.001          0.072727273
0.001  0.9  0.81           0.00081      0.00181        0.131636364
0.001  0.8  0.64           0.00064      0.00245        0.178181818
0.001  0.7  0.49           0.00049      0.00294        0.213818182
0.001  0.6  0.36           0.00036      0.0033         0.24
0.001  0.5  0.25           0.00025      0.00355        0.258181818
0.001  0.4  0.16           0.00016      0.00371        0.269818182
0.001  0.3  0.09           0.00009      0.0038         0.276363636
0.001  0.2  0.04           0.00004      0.00384        0.279272727
0.991  0.1  0.01           0.00991      0.01375        1

                  P(o=2/2) 0.01375                

今ではP(f >= 0.5)、以前の想定であった0.6%から26%になっています。したがって、ベイズでは、の推定コストの見積もりを更新できますI。データの量が少ない場合、コストが正確にわかるわけではなく、修正する価値があるだけの大きさであるだけです。

それを見るさらに別の方法は、継承のルールと呼ばれます。コインを2回裏返し、両方の時間に表に出た場合、コインの予想される重みについて何がわかりますか?尊敬される答えの方法は、それが平均値のベータ分布であると言うことです(number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%

(重要なのは、I複数回表示されることです。1回しか表示されない場合は、f> 0であることを除いて、あまりわかりません。)

したがって、サンプルの数が非常に少ない場合でも、表示される命令のコストについて多くのことを知ることができます。(そして、それは場合。そのコストに比例し、平均で、頻度でそれらを見ることができますnサンプルが取られ、そしてfコストで、その後Iに表示されますnf+/-sqrt(nf(1-f))サンプル。例、n=10f=0.3、つまり3+/-1.4、サンプル)。


追加:測定とランダムスタックサンプリングの違いを直感的に感じるために:
壁時計時間でもスタックをサンプリングするプロファイラーがありますが、出てくるのは測定(またはホットパス、またはホットスポット)です。 「ボトルネック」は簡単に隠すことができます)。彼らがあなたに見せない(そして彼らは簡単にできる)のは実際のサンプルそのものです。そして、あなたの目標がボトルネックを見つけることである場合、あなたが見る必要があるそれらの数は、平均して、2をそれがかかる時間の割合で割ったものです。したがって、30%の時間がかかる場合、平均して2 / .3 = 6.7サンプルで表示され、20サンプルで表示される可能性は99.2%です。

これは、測定値の検査とスタックサンプルの検査の違いをすぐに説明したものです。ボトルネックは、このような1つの大きなブロブ、または多数の小さなブロブである可能性がありますが、違いはありません。

測定は水平です。特定のルーチンにかかる時間の割合を示します。サンプリングは垂直です。プログラム全体がその時点で実行していることを回避する方法があり、2番目のサンプルでそれを確認した場合、ボトルネックが見つかりました。それが違いを生むものです-どれだけの時間だけでなく、費やされた時間の全体的な理由を見てください。

610
Ajay 2009-04-21 18:09.

Valgrindは次のオプションで使用できます

valgrind --tool=callgrind ./(Your binary)

と呼ばれるファイルを生成しますcallgrind.out.x。その後、kcachegrindツールを使用してこのファイルを読み取ることができます。それはあなたに物事のグラフィカルな分析を与え、どの行がどれくらいの費用がかかるかなどの結果をもたらします。

362
Nazgob 2008-12-18 10:34.

GCCを使用していると思います。標準的な解決策は、gprofでプロファイリングすることです。

-pgプロファイリングの前に、必ずコンパイルに追加してください。

cc -o myprog myprog.c utils.c -g -pg

私はまだそれを試していませんが、google-perftoolsについて良いことを聞いています。それは間違いなく試してみる価値があります。

ここに関連する質問。

うまくgprofいかない場合は、他のいくつかの流行語:Valgrind、Intel VTune、SunDTrace。

263
Will 2010-08-18 01:48.

新しいカーネル(最新のUbuntuカーネルなど)には、新しい「perf」ツール(apt-get install linux-tools)AKAperf_eventsが付属しています。

これらには、古典的なサンプリングプロファイラー(manページ)と素晴らしいタイムチャートが付属しています!

重要なことは、これらのツールはプロセスプロファイリングだけでなくシステムプロファイリングでもかまいません。スレッド、プロセス、カーネル間の相互作用を示し、プロセス間のスケジューリングとI / Oの依存関係を理解できるようにすることができます。

78
Noname 2009-05-23 11:44.

プロファイリングツールスイートのベースとしてValgrindとCallgrindを使用します。知っておくべき重要なことは、Valgrindは基本的に仮想マシンであるということです。

(ウィキペディア)Valgrindは、本質的に、動的再コンパイルを含むジャストインタイム(JIT)コンパイル手法を使用する仮想マシンです。元のプログラムの内容がホストプロセッサで直接実行されることはありません。代わりに、Valgrindは最初に、プログラムを中間表現(IR)と呼ばれる一時的で単純な形式に変換します。これは、プロセッサーに依存しないSSAベースの形式です。変換後、ValgrindがIRをマシンコードに変換してホストプロセッサに実行させる前に、ツール(以下を参照)はIRで必要な変換を自由に実行できます。

Callgrindは、その上に構築されたプロファイラーです。主な利点は、信頼できる結果を得るためにアプリケーションを何時間も実行する必要がないことです。Callgrindはプロファイリングを行わないプロファイラーであるため、1秒の実行でも、堅実で信頼性の高い結果を得るのに十分です。

Valgrindに基づいて構築された別のツールはMassifです。ヒープメモリ使用量のプロファイルに使用します。それは素晴らしい働きをします。それが行うことは、メモリ使用量のスナップショットを提供することです-詳細情報何がメモリの何パーセントを保持し、誰がそれをそこに置いたか。このような情報は、アプリケーションの実行のさまざまな時点で利用できます。

75
Tõnu Samuel 2012-06-08 22:01.

実行するための答えは、valgrind --tool=callgrindいくつかのオプションなしでは完全ではありません。通常、Valgrindで10分間の遅い起動時間をプロファイリングしたくないので、プログラムが何らかのタスクを実行しているときにプロファイリングしたいと思います。

だからこれが私がお勧めするものです。最初にプログラムを実行します。

valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no ./binary > tmp

これで動作し、プロファイリングを開始したい場合は、別のウィンドウで実行する必要があります。

callgrind_control -i on

これにより、プロファイリングがオンになります。それをオフにしてタスク全体を停止するには、次のようにします。

callgrind_control -k

これで、現在のディレクトリにcallgrind.out。*という名前のファイルがいくつかあります。プロファイリング結果を表示するには、次を使用します。

kcachegrind callgrind.out.*

次のウィンドウで「Self」列ヘッダーをクリックすることをお勧めします。クリックしないと、「main()」が最も時間のかかるタスクであることがわかります。「自己」は、扶養家族と一緒ではなく、各機能自体にどれだけの時間がかかったかを示します。

62
Rob_before_edits 2011-07-01 09:30.

これは、NazgobのGprofの回答に対する回答です。

私はここ数日Gprofを使用していて、すでに3つの重要な制限を発見しましたが、そのうちの1つは他のどこにも(まだ)文書化されていません。

  1. 回避策を使用しない限り、マルチスレッドコードでは正しく機能しません

  2. コールグラフは関数ポインタによって混乱します。例:multithread()指定された配列(両方とも引数として渡される)上で指定された関数をマルチスレッド化できるようにするという関数があります。ただし、Gprofは、multithread()子供に費やされた時間を計算する目的で、へのすべての呼び出しを同等と見なします。私が渡す関数の中には、他の関数multithread()よりもはるかに時間がかかるものがあるため、コールグラフはほとんど役に立ちません。(ここでスレッド化が問題であるかどうか疑問に思っている人へ:いいえ、multithread()オプションでできます。この場合は、呼び出し元のスレッドでのみすべてを順番に実行します)。

  3. ここでは、「...呼び出し数の数値は、サンプリングではなくカウントによって得られます。完全に正確です...」と書かれています。それでも、コールグラフは、最初の番号が直接呼び出しであり、2番目の再帰呼び出し(すべてそれ自体からのもの)である、最も呼び出された関数への呼び出し統計として5345859132 +784984078を示していることがわかります。これはバグがあることを意味するので、長い(64ビット)カウンターをコードに入れて、同じ実行を再度実行しました。私のカウント:5345859132直接、および78094395406自己再帰呼び出し。そこにはたくさんの数字があるので、私が測定する再帰呼び出しは780億であるのに対し、Gprofからは784mであり、100倍異なることを指摘します。両方の実行はシングルスレッドで最適化されていないコードであり、一方はコンパイルさ-gれ、もう一方はコンパイルされました-pg

これは、GNUたgprofはそれが誰を助けている場合、64ビットのDebian Lennyの下で実行されている(DebianのためのGNU Binutilsの)2.18.0.20080103。

26
Ehsan 2017-02-24 11:28.

Valgrind、callgrind、kcachegrindを使用します。

valgrind --tool=callgrind ./(Your binary)

callgrind.out.xを生成します。kcachegrindを使用して読んでください。

gprofを使用する(-pgを追加):

cc -o myprog myprog.c utils.c -g -pg 

(マルチスレッド、関数ポインターにはあまり適していません)

google-perftoolsを使用します。

タイムサンプリングを使用すると、I / OとCPUのボトルネックが明らかになります。

Intel VTuneは最高です(教育目的では無料)。

その他: AMD Codeanalyst(AMD CodeXLに置き換えられたため)、OProfile、「perf」ツール(apt-get install linux-tools)

C ++プロファイリング手法の調査:gprof vs valgrind vs perf vs gperftools

この回答では、いくつかの異なるツールを使用して、いくつかの非常に単純なテストプログラムを分析し、それらのツールがどのように機能するかを具体的に比較します。

次のテストプログラムは非常に単純で、次のことを行います。

  • main通話fastmaybe_slow3回、maybe_slow通話の1つが遅い

    の遅い呼び出しmaybe_slowは10倍長く、子関数の呼び出しを考慮すると実行時間を支配しますcommon。理想的には、プロファイリングツールが特定のスローコールを示すことができるようになります。

  • fastmaybe_slowcallの両方common。これは、プログラム実行の大部分を占めます。

  • プログラムインターフェイスは次のとおりです。

    ./main.out [n [seed]]
    

    プログラムはO(n^2)合計でループを実行します。seedランタイムに影響を与えることなく、異なる出力を取得するだけです。

main.c

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __attribute__ ((noinline)) common(uint64_t n, uint64_t seed) {
    for (uint64_t i = 0; i < n; ++i) {
        seed = (seed * seed) - (3 * seed) + 1;
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) fast(uint64_t n, uint64_t seed) {
    uint64_t max = (n / 10) + 1;
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) maybe_slow(uint64_t n, uint64_t seed, int is_slow) {
    uint64_t max = n;
    if (is_slow) {
        max *= 10;
    }
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

int main(int argc, char **argv) {
    uint64_t n, seed;
    if (argc > 1) {
        n = strtoll(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    if (argc > 2) {
        seed = strtoll(argv[2], NULL, 0);
    } else {
        seed = 0;
    }
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 1);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    printf("%" PRIX64 "\n", seed);
    return EXIT_SUCCESS;
}

gprof

gprofは、インストルメンテーションを使用してソフトウェアを再コンパイルする必要があり、そのインストルメンテーションと一緒にサンプリングアプローチも使用します。したがって、精度(サンプリングが常に完全に正確であるとは限らず、関数をスキップできる)と実行の速度低下(計測とサンプリングは実行をそれほど遅くしない比較的高速な手法)のバランスを取ります。

gprofはGCC / binutilsに組み込まれ-pgているため、gprofを有効にするオプションを指定してコンパイルするだけです。次に、数秒の妥当な期間の実行を生成するサイズCLIパラメーターを使用してプログラムを通常どおり実行します(10000):

gcc -pg -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time ./main.out 10000

教育上の理由から、最適化を有効にせずに実行も行います。通常、最適化されたプログラムのパフォーマンスの最適化のみに関心があるため、これは実際には役に立たないことに注意してください。

gcc -pg -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 10000

まず、time実行時間の有無-pgが同じであることがわかります。これはすばらしいことです。速度低下はありません。ただし、このチケットに示されているように、複雑なソフトウェアで2倍から3倍の速度低下のアカウントを見てきました。

でコンパイルしたため-pg、プログラムを実行すると、gmon.outプロファイリングデータを含むファイルファイルが生成されます。

次の質問gprof2dotに従って、そのファイルをグラフィカルに観察できます。gprofの結果をグラフィカルに表現することは可能ですか?

sudo apt install graphviz
python3 -m pip install --user gprof2dot
gprof main.out > main.gprof
gprof2dot < main.gprof | dot -Tsvg -o output.svg

ここで、gprofツールはgmon.outトレース情報を読み取り、で人間が読める形式のレポートを生成します。次にmain.gprof、をgprof2dot読み取ってグラフを生成します。

gprof2dotのソースは次のとおりです。 https://github.com/jrfonseca/gprof2dot

-O0実行では、次のことがわかります。

そして-O3実行のために:

-O0出力はかなり自明です。たとえば、3つのmaybe_slow呼び出しとその子呼び出しが、合計実行時間の97.56%を占めることを示していますが、子maybe_slowなしでの実行は、合計実行時間の0.00%を占めています。つまり、その関数に費やされた時間のほとんどすべてが子が呼び出します。

TODO:GDBで表示できるのにmain、なぜ-O3出力に表示されbtないのですか?GProf出力から関数が欠落しているのは、gprofがコンパイルされたインストルメンテーションに加えてサンプリングベースでもあり、-O3 mainが速すぎてサンプルがないためだと思います。

SVGはCtrl + Fで検索可能であり、ファイルサイズは約10分の1になる可能性があるため、PNGではなくSVG出力を選択します。また、生成された画像の幅と高さは、複雑なソフトウェアの場合は数万ピクセルと非常に大きくなる可能性がありeog、その場合、PNGの場合はGNOME 3.28.1のバグが発生しますが、SVGはブラウザによって自動的に開かれます。gimp 2.8はうまく機能しましたが、以下も参照してください。

  • https://askubuntu.com/questions/1112641/how-to-view-extremely-large-images
  • https://unix.stackexchange.com/questions/77968/viewing-large-image-on-linux
  • https://superuser.com/questions/356038/viewer-for-huge-images-under-linux-100-mp-color-images

しかし、それでも、画像をドラッグして必要なものを見つけることができます。たとえば、このチケットから取得した「実際の」ソフトウェアの例からのこの画像を参照してください。

並べ替えられていない小さなスパゲッティラインが互いに重なり合って、最も重要なコールスタックを簡単に見つけることができますか?dot確かにもっと良い選択肢があるかもしれませんが、今は行きたくありません。私たちが本当に必要としているのは、そのための適切な専用ビューアですが、まだ見つけていません。

  • kcachegrindでgprof出力を表示する
  • KProfの最良の代替品はどれですか?

ただし、カラーマップを使用して、これらの問題を少し軽減することができます。たとえば、前の巨大な画像では、緑が赤の後に続き、最後に濃い青が続くという見事な推論を行ったときに、最終的に左側のクリティカルパスを見つけることができました。

または、gprof以前に保存した組み込みのbinutilsツールのテキスト出力を確認することもできます。

cat main.gprof

デフォルトでは、これにより、出力データの意味を説明する非常に詳細な出力が生成されます。それ以上の説明はできないので、自分で読んでもらいましょう。

データ出力形式を理解したら、-bオプションを使用してチュートリアルなしでデータのみを表示するように冗長性を減らすことができます。

gprof -b main.out

この例では、出力は次の-O0とおりです。

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
100.35      3.67     3.67   123003     0.00     0.00  common
  0.00      3.67     0.00        3     0.00     0.03  fast
  0.00      3.67     0.00        3     0.00     1.19  maybe_slow

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.27% of 3.67 seconds

index % time    self  children    called     name
                0.09    0.00    3003/123003      fast [4]
                3.58    0.00  120000/123003      maybe_slow [3]
[1]    100.0    3.67    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    3.67                 main [2]
                0.00    3.58       3/3           maybe_slow [3]
                0.00    0.09       3/3           fast [4]
-----------------------------------------------
                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]
-----------------------------------------------
                0.00    0.09       3/3           main [2]
[4]      2.4    0.00    0.09       3         fast [4]
                0.09    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common                  [4] fast                    [3] maybe_slow

および-O3

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
100.52      1.84     1.84   123003    14.96    14.96  common

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.54% of 1.84 seconds

index % time    self  children    called     name
                0.04    0.00    3003/123003      fast [3]
                1.79    0.00  120000/123003      maybe_slow [2]
[1]    100.0    1.84    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     97.6    0.00    1.79                 maybe_slow [2]
                1.79    0.00  120000/123003      common [1]
-----------------------------------------------
                                                 <spontaneous>
[3]      2.4    0.00    0.04                 fast [3]
                0.04    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common

各セクションの非常に簡単な要約として、例:

                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]

インデントされたままの関数を中心にしています(maybe_flow)。[3]その関数のIDです。関数の上には呼び出し元があり、関数の下には呼び出し先があります。

以下のために-O3、というグラフィカルな出力のようにここで見るmaybe_slowと、fastドキュメントがその言うことで知られている親、持っていない<spontaneous>手段を。

gprofを使用して行ごとのプロファイリングを行うための優れた方法があるかどうかはわかりません。特定のコード行で費やされた `gprof`時間

valgrind callgrind

valgrindは、valgrind仮想マシンを介してプログラムを実行します。これにより、プロファイリングが非常に正確になりますが、プログラムの速度が大幅に低下します。以前にkcachegrindについても言及しました:コードの画像関数呼び出しグラフを取得するためのツール

callgrindはコードをプロファイルするvalgrindのツールであり、kcachegrindはcachegrindの出力を視覚化できるKDEプログラムです。

最初に-pgフラグを削除して通常のコンパイルに戻る必要があります。そうしないと、実行は実際にはで失敗しProfiling timer expiredます。そうです、これは非常に一般的であるため、スタックオーバーフローの質問がありました。

したがって、次のようにコンパイルして実行します。

sudo apt install kcachegrind valgrind
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time valgrind --tool=callgrind valgrind --dump-instr=yes \
  --collect-jumps=yes ./main.out 10000

これにより--dump-instr=yes --collect-jumps=yes、比較的小さな追加のオーバーヘッドコストで、組立ラインごとのパフォーマンスの内訳を表示できる情報もダンプされるため、有効にします。

バット、timeプログラムを実行するために29.5秒かかったので、私たちは、この例の15倍程度の減速を持っていたことを教えてくれる。明らかに、この速度低下は、より大きなワークロードにとって深刻な制限になるでしょう。ここで説明した「実際のソフトウェアの例」では、80倍の速度低下が見られました。

実行すると、callgrind.out.<pid>たとえばcallgrind.out.8554私の場合、という名前のプロファイルデータファイルが生成されます。そのファイルを次のように表示します。

kcachegrind callgrind.out.8554

これは、テキストのgprof出力と同様のデータを含むGUIを示しています。

また、右下の[コールグラフ]タブに移動すると、右クリックしてエクスポートできるコールグラフが表示され、不当な量の白い境界線を持つ次の画像が取得されます:-)

fastkcachegrindは、呼び出しにかかる時間が短すぎるため、視覚化を単純化したに違いないため、そのグラフには表示されていないと思います。これは、実際のプログラムで必要な動作になる可能性があります。右クリックメニューには、そのようなノードをいつカリングするかを制御するためのいくつかの設定がありますが、すばやく試行した後、そのような短い呼び出しを表示することができませんでした。fast左側のウィンドウをクリックすると、が付いたコールグラフが表示されるfastため、スタックが実際にキャプチャされました。完全なグラフ呼び出しグラフを表示する方法をまだ誰も見つけていませんでした。callgrindにkcachegrindコールグラフ内のすべての関数呼び出しを表示させる

複雑なC ++ソフトウェアのTODOは、私はタイプのいくつかのエントリを参照してください<cycle N>例えば、<cycle 11>私はそれがどういう意味、関数名を期待したいところ?オンとオフを切り替える「サイクル検出」ボタンがあることに気づきましたが、それはどういう意味ですか?

perf から linux-tools

perfLinuxカーネルのサンプリングメカニズムのみを使用しているようです。これにより、セットアップが非常に簡単になりますが、完全に正確ではありません。

sudo apt install linux-tools
time perf record -g ./main.out 10000

これにより実行に0.2秒が追加されたため、時間的には問題ありませんcommonが、キーボードの右矢印でノードを展開した後でも、あまり関心がありません。

Samples: 7K of event 'cycles:uppp', Event count (approx.): 6228527608     
  Children      Self  Command   Shared Object     Symbol                  
-   99.98%    99.88%  main.out  main.out          [.] common              
     common                                                               
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.01%     0.01%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.01%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.01%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.01%     0.00%  main.out  ld-2.27.so        [.] mprotect            
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.01%     0.00%  main.out  ld-2.27.so        [.] _xstat              
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x2f3d4f4944555453  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007fff3cfc57ac  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

それで、-O0プログラムのベンチマークを試みて、それが何かを示しているかどうかを確認します。そしてついに、コールグラフが表示されます。

Samples: 15K of event 'cycles:uppp', Event count (approx.): 12438962281   
  Children      Self  Command   Shared Object     Symbol                  
+   99.99%     0.00%  main.out  [unknown]         [.] 0x04be258d4c544155  
+   99.99%     0.00%  main.out  libc-2.27.so      [.] __libc_start_main   
-   99.99%     0.00%  main.out  main.out          [.] main                
   - main                                                                 
      - 97.54% maybe_slow                                                 
           common                                                         
      - 2.45% fast                                                        
           common                                                         
+   99.96%    99.85%  main.out  main.out          [.] common              
+   97.54%     0.03%  main.out  main.out          [.] maybe_slow          
+    2.45%     0.00%  main.out  main.out          [.] fast                
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.00%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.00%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_lookup_symbol_x 
     0.00%     0.00%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.00%     0.00%  main.out  ld-2.27.so        [.] mmap64              
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x552e53555f6e653d  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007ffe1cf20fdb  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

TODO:-O3実行時に何が起こったのですか?それは単にそれでmaybe_slowあり、fast速すぎてサンプルを取得できませんでしたか?-O3実行に時間がかかる大きなプログラムでうまく機能しますか?CLIオプションを見逃しましたか?-Fヘルツでサンプル周波数を制御しようとしていることがわかりましたが、デフォルトで許可されている最大値-F 39500(で増やすことができますsudo)まで上げましたが、それでも明確な呼び出しが表示されません。

perfすばらしい点の1つは、Brendan GreggのFlameGraphツールです。このツールは、コールスタックのタイミングを非常にわかりやすく表示し、大きなコールをすばやく確認できるようにします。このツールは次の場所で入手できます。https://github.com/brendangregg/FlameGraph また、彼のパフォーマンスチュートリアルでも言及されています。 http://www.brendangregg.com/perf.html#FlameGraphs私がそうするperfことなく走ったとき、今のところ私はそれをやっています:sudoERROR: No stack counts foundsudo

git clone https://github.com/brendangregg/FlameGraph
sudo perf record -F 99 -g -o perf_with_stack.data ./main.out 10000
sudo perf script -i perf_with_stack.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

しかし、そのような単純なプログラムでは、出力maybe_slowfastそのグラフにも簡単に表示されないため、出力を理解するのは簡単ではありません。

より複雑な例では、グラフの意味が明らかになります。

TODO[unknown]その例には関数のログがありますが、それはなぜですか?

価値があるかもしれない別のパフォーマンスGUIインターフェースは次のとおりです。

  • Eclipse Trace Compassプラグイン: https://www.eclipse.org/tracecompass/

    ただし、これには、最初にデータをCommon Trace Formatに変換する必要があるという欠点があります。これは、で実行できますがperf data --to-ctf、ビルド時に有効にするperfか、十分に新しいものにする必要があります。どちらも、のパフォーマンスには当てはまりません。 Ubuntu 18.04

  • https://github.com/KDAB/hotspot

    これの欠点は、Ubuntuパッケージがないようであり、Ubuntu18.04がQt5.9であるのに対し、それをビルドするにはQt5.10が必要なことです。

gperftools

以前は「Googleパフォーマンスツール」と呼ばれていました。出典: https://github.com/gperftools/gperftools サンプルベース。

最初に次のコマンドでgperftoolsをインストールします。

sudo apt install google-perftools

次に、実行時またはビルド時の2つの方法でgperftoolsCPUプロファイラーを有効にできます。

実行時に、set LD_PRELOADto pointを渡すlibprofiler.so必要があります。これはlocate libprofiler.so、たとえば私のシステムで見つけることができます。

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libprofiler.so \
  CPUPROFILE=prof.out ./main.out 10000

または、リンク時にライブラリをビルドして、LD_PRELOAD実行時にパスをディスペンスすることもできます。

gcc -Wl,--no-as-needed,-lprofiler,--as-needed -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
CPUPROFILE=prof.out ./main.out 10000

参照:gperftools-プロファイルファイルがダンプされない

私がこれまでに見つけたこのデータを表示する最も良い方法は、pprof出力をkcachegrindが入力として受け取るのと同じ形式(はい、Valgrind-project-viewer-tool)にし、kcachegrindを使用してそれを表示することです。

google-pprof --callgrind main.out prof.out  > callgrind.out
kcachegrind callgrind.out

これらの方法のいずれかで実行した後prof.out、出力としてプロファイルデータファイルを取得します。そのファイルをSVGとしてグラフィカルに表示できます。

google-pprof --web main.out prof.out

これは、他のツールと同じように使い慣れたコールグラフとして提供されますが、秒ではなくサンプル数の不格好な単位を使用します。

または、次の方法でテキストデータを取得することもできます。

google-pprof --text main.out prof.out

これは:

Using local file main.out.
Using local file prof.out.
Total: 187 samples
     187 100.0% 100.0%      187 100.0% common
       0   0.0% 100.0%      187 100.0% __libc_start_main
       0   0.0% 100.0%      187 100.0% _start
       0   0.0% 100.0%        4   2.1% fast
       0   0.0% 100.0%      187 100.0% main
       0   0.0% 100.0%      183  97.9% maybe_slow

参照:グーグルパフォーマンスツールの使用方法

生のperf_event_openシステムコールでコードをインストルメント化する

これは、をperf使用する基本的なサブシステムと同じだと思いますが、もちろん、コンパイル時に対象のイベントを明示的にインストルメント化することで、さらに優れた制御を実現できます。

これはほとんどの人にとってハードコアすぎますが、ちょっと楽しいです。実行可能な最小限の例:Cプログラムで実行された命令の数をカウントする簡単な方法

Ubuntu 18.04、gprof2dot 2019.11.30、valgrind 3.13.0、perf 4.15.18、Linuxカーネル4.15.0、FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b、gperftools2.5-2でテスト済み。

6
fwyzard 2018-03-18 02:20.

シングルスレッドプログラムの場合、igprof、The IgnominousProfilerを使用できます。https://igprof.org/ 。

これは、Mike Dunlaveyによる...長い...回答に沿ったサンプリングプロファイラーであり、結果を閲覧可能な呼び出しスタックツリーにギフト包装し、各関数で費やされた時間またはメモリを累積または関数ごと。

5
raovgarimella 2018-09-15 12:56.

また、言及する価値があります

  1. HPCToolkit(http://hpctoolkit.org/)-オープンソース、並列プログラムで動作し、結果を複数の方法で確認するためのGUIを備えています
  2. Intel VTune(https://software.intel.com/en-us/vtune)-Intelコンパイラを使用している場合、これは非常に優れています
  3. タウ(http://www.cs.uoregon.edu/research/tau/home.php)

私はHPCToolkitとVTuneを使用しましたが、テント内の長いポールを見つけるのに非常に効果的であり、コードを再コンパイルする必要はありません(ただし、意味のある出力を取得するには、CMakeで-g -OまたはRelWithDebInfoタイプのビルドを使用する必要があります) 。TAUの機能は似ていると聞きました。

4
seo 2013-11-29 08:21.

コードを高速化するために使用する2つの方法は次のとおりです。

CPUバウンドアプリケーションの場合:

  1. DEBUGモードでプロファイラーを使用して、コードの疑わしい部分を特定します
  2. 次に、RELEASEモードに切り替えて、パフォーマンスの変化が見られるまで、コードの疑わしいセクションをコメントアウトします(何も付けずにスタブします)。

I / Oバウンドアプリケーションの場合:

  1. RELEASEモードでプロファイラーを使用して、コードの疑わしい部分を特定します。

NB

プロファイラーがない場合は、貧乏人のプロファイラーを使用してください。アプリケーションのデバッグ中に一時停止を押します。ほとんどの開発者スイートは、コメント付きの行番号でアセンブリに分割されます。統計的には、CPUサイクルのほとんどを消費している地域に着陸する可能性があります。

CPUの場合、DEBUGモードでプロファイリングを行う理由は、RELEASEモードでプロファイリングを試みた場合、コンパイラーが数学を減らし、ループをベクトル化し、インライン関数を実行するため、コードがアセンブルされたときにマップできない混乱に陥る傾向があるためです。マップできない混乱は、アセンブリが最適化中のソースコードに対応していない可能性があるため、プロファイラーがこれほど時間がかかっているものを明確に識別できないことを意味しますRELEASEモードのパフォーマンス(タイミングセンシティブなど)が必要な場合は、必要に応じてデバッガー機能を無効にして、使用可能なパフォーマンスを維持します。

I / Oバウンドの場合、I / O操作は共有ライブラリに外部からリンクされているか(ほとんどの場合)、最悪の場合、システムが生成されるため、プロファイラーはRELEASEモードでI / O操作を識別できます。割り込みベクタを呼び出します(プロファイラーでも簡単に識別できます)。

3
BullyWiiPlaza 2019-05-22 03:28.

loguruプロファイリングにうまく使用できるタイムスタンプと合計稼働時間が含まれているため、次のようなロギングフレームワークを使用できます。

2
N3UR0CHR0M 2019-02-25 08:01.

iprofライブラリを使用できます。

https://gitlab.com/Neurochrom/iprof

https://github.com/Neurochrom/iprof

クロスプラットフォームであり、アプリケーションのパフォーマンスをリアルタイムで測定しないようにすることができます。ライブグラフと組み合わせることもできます。完全な免責事項:私は著者です。

2
SOKS 2019-05-18 00:13.

仕事では、スケジューリングの観点から必要なものを監視するのに役立つ本当に素晴らしいツールがあります。これは何度も役に立ちました。

これはC ++であり、ニーズに合わせてカスタマイズする必要があります。残念ながら、コードを共有することはできません。概念だけを共有できます。volatileタイムスタンプとイベントIDを含む「大きな」バッファを使用します。これは、事後分析またはログシステムの停止後にダンプできます(たとえば、これをファイルにダンプします)。

すべてのデータを含むいわゆる大きなバッファを取得すると、小さなインターフェイスがそれを解析し、オシロスコープが色(.hppファイルで構成)で行うように、名前(上/下+値)でイベントを表示します。

生成されるイベントの量をカスタマイズして、必要なものだけに焦点を合わせます。1秒あたりにログに記録されたイベントの量に基づいて必要なCPUの量を消費しながら、スケジューリングの問題に大いに役立ちました。

3つのファイルが必要です:

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

コンセプトは、次のようにイベントを定義するtool_events_id.hppことです。

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

また、次の場所でいくつかの関数を定義しますtoolname.hpp

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

コードのどこでも使用できます:

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

このprobe関数は、いくつかの組立ラインを使用してクロックタイムスタンプをできるだけ早く取得し、バッファにエントリを設定します。ログイベントを格納するインデックスを安全に見つけるためのアトミックインクリメントもあります。もちろん、バッファは循環しています。

サンプルコードの不足によってアイデアが難読化されないことを願っています。

2
u__ 2019-11-04 04:47.

実際、グーグル/ベンチマークについてはあまり言及されていませんが、コードの特定の領域を固定するのは少し面倒ですが、特にコードベースが少し大きい場合は少し驚きますが、これを組み合わせて使用​​すると非常に役立ちますcallgrind

ここで重要なのは、ボトルネックの原因となっている部分を特定するIMHOです。ただし、最初に次の質問に答えて、それに基づいてツールを選択します

  1. 私のアルゴリズムは正しいですか?
  2. ボトルネックであることが証明されているロックはありますか?
  3. 犯人であることが証明されているコードの特定のセクションはありますか?
  4. IO、処理、最適化はどうですか?

valgrindとの組み合わせでcallrindkcachegrind上記の点について適切な見積もりを提供する必要があります。コードの一部に問題があることが確認されたら、マイクロベンチマークgoogle benchmarkを開始することをお勧めします。

1
Mehdi_Pejvak 2019-12-08 02:52.

-pgコードをコンパイルおよびリンクするときにフラグを使用し、実行可能ファイルを実行します。このプログラムの実行中、プロファイリングデータはファイルa.outに収集されます。
プロファイリングには2つの異なるタイプがあります

1-フラットプロファイリング:
コマンドgprog --flat-profile a.outを実行すると、次のデータが得られます
-関数に費やされた時間全体の何パーセント
か-関数に費やされた秒
数-サブ関数の呼び出しを含めて除外します-呼び出し、
-呼び出しごとの平均時間。

2-以下を含む各関数の次のデータを取得
するコマンドgprof --graph a.outをプロファイリングするグラフ
-各セクションでは、1つの関数にインデックス番号が付けられています。
-関数の上に、関数を呼び出す関数のリストがあります。
-関数の下に、関数によって呼び出される関数のリストがあります。

詳細については、こちらをご覧ください https://sourceware.org/binutils/docs-2.32/gprof/

1
Noname 2020-04-19 09:23.

デバッグソフトウェアを使用して、コードの実行速度が遅い場所を特定する方法を教えてください。

移動中に障害物があると思ってください。速度が低下します。

そのような不要な再割り当てのループ、バッファオーバーフロー、検索、メモリリークなどの操作は、より多くの実行電力を消費し、コードのパフォーマンスに悪影響を及ぼします。プロファイリングの前に、コンパイルに-pgを追加してください。

g++ your_prg.cpp -pgまたはcc my_program.cpp -g -pgコンパイラごとに

まだ試していませんが、google-perftoolsについて良いことを聞いています。それは間違いなく試してみる価値があります。

valgrind --tool=callgrind ./(Your binary)

gmon.outまたはcallgrind.out.xというファイルが生成されます。その後、kcachegrindまたはデバッガツールを使用してこのファイルを読み取ることができます。それはあなたに物事のグラフィカルな分析を与え、どの行がどれくらいの費用がかかるかなどの結果をもたらします。

私はそう思う

0
Wei 2019-06-28 18:44.

Arm MAPについては誰も言及していなかったので、個人的にはMapを使用してC ++科学プログラムのプロファイルを作成することに成功したので追加します。

Arm MAPは、パラレル、マルチスレッド、またはシングルスレッドのC、C ++、Fortran、およびF90コードのプロファイラーです。これは、詳細な分析とソースラインへのボトルネックの特定を提供します。ほとんどのプロファイラーとは異なり、並列およびスレッド化されたコードのpthread、OpenMP、またはMPIをプロファイリングできるように設計されています。

MAPは商用ソフトウェアです。

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

ねえNFL、ジョーバロウとカイラーマレーは女性の権利をサポートするために少しの助けを使うことができます

ねえNFL、ジョーバロウとカイラーマレーは女性の権利をサポートするために少しの助けを使うことができます

ジョー・バロウロー対ウェイド事件の転覆に対応するNFLは、言葉を言わないことで、腹立たしいが予測可能なPRの結果でした。

別の日、別のヒンジのないLIV記者会見

別の日、別のヒンジのないLIV記者会見

(lから)パット・ペレス、ブルックス・ケプカ、パトリック・リードサウジアラビアのLIVゴルフリーグのさらに別の信じられないほどの記者会見で、スポーツのファンはブルックス・ケプカからでたらめな吐き気と質問回避の驚異的なマスタークラスを受けました。パトリックリード、ブライソンデシャンボー、パットペレス、最近のPGAツアーの脱北者。

ミズ・マーベルの家族の帰郷は悪役よりも激しく打撃を与える

ミズ・マーベルの家族の帰郷は悪役よりも激しく打撃を与える

レッドダガーとマーベルさんがチームを組んでいます。

6億7500万ドルのビットコインローンのデフォルト後にすべての資産を清算するように命じられたスリーアローズキャピタル

6億7500万ドルのビットコインローンのデフォルト後にすべての資産を清算するように命じられたスリーアローズキャピタル

暗号業界最大の沈没船の1つであるスリーアローズキャピタルは、ついにその悲惨さから解放されています。火曜日に、不良債権ヘッジファンドは、債権者からの返済を要求する訴訟の高まりに応えて、バージンアイランド裁判所によって清算を命じられました彼らが3ACに行ったローン。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

Suffragettes Indicam #3: Junho

Suffragettes Indicam #3: Junho

Mais um mês se findando — e metade do ano de 2022 já passou. Sabe o que isso significa? Não, não é hora de verificar se você está cumprindo com suas resoluções de Ano Novo.

多元宇宙—Junø

多元宇宙—Junø

チェーン間アカウントがJunoに登場します。異なるブロックチェーン間でスマートコントラクトの構成可能性と真の相互運用性を提供します。

#brand【ベター・コール・ソール!アメリカのテレビシリーズ「ブレイキング・バッド」に最高のビジネス例が隠されている】・・・ルールクリエイティブ

#brand【ベター・コール・ソール!アメリカのテレビシリーズ「ブレイキング・バッド」に最高のビジネス例が隠されている】・・・ルールクリエイティブ

1.ドラマを見た後、起業する考えはありますか?あなたのビジネスはボトルネックに遭遇しましたか?方向性がなくてわからない場合は、ドラマを追いかけて行くことを心からお勧めします。(?)ブラフではなく、最も完璧なビジネス例を隠すドラマがあります。2.ブレイキング・バッドとその弁護士ドラマ「ブレイキング・バッド」を見た友人たちは、演劇の中で、穏やかな表情で、弁護士のソウル・グッドマンに深く感銘を受けなければなりません。口を開けて、感覚の弱い傭兵の性格を持っています。道徳の面で、サル・グッドマンは無意識のうちに劇に欠かせない役割を果たし、彼自身のシリーズ「絶望的な弁護士」(ベター・コール・ソール)を生み出しました。ウェントウのテキストとビデオは、劇中のソウル・グッドマンのテレビコマーシャルです。製品(サービス)、競争戦略、市場ポジショニング、ブランド名、ターゲット顧客グループ、コミュニケーション軸から広告まで、サル・グッドマンの役割のビジネス設定は、「最低」と見なすことができる超超超超超超完全です。ブランドコミュニケーションのコスト」「変化」のモデル。なぜ?私の分析をご覧ください。3.ソウル・グッドマンの「事業戦略」1.基本情報ブランド名:Saul Goodman製品:法律相談サービス対象顧客:麻薬中毒、飲酒運転、事故など。法律知識の欠如は、一般的に公立弁護士にしか余裕がなく、真面目な弁護士も「特別な法律を持つ消費者」を避けます。恐れてはいけない「​​ニーズ」。コミュニケーションの主軸:この国のすべての男性、女性、子供は有罪判決を受けるまで無実だと思います。地域:アルバカーキ市スローガン:Thrallに電話したほうがいいです!(ベター・コール・ソール)広告:2つの可能性のある犯罪状況をシミュレートします+サウルの主張+サウルのスローガン2をより適切に呼び出します。

メインネットガイド— Arbitrum Odyssey Week 2

メインネットガイド— Arbitrum Odyssey Week 2

最新のアップデートを受け取るために私たちに従ってください。ニュースレター:https://www。

Language