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

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

ただいまの
回答率

90.34%

  • Linux

    4030questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

clock_gettime()を使った実行時間計測時の差について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,033

for文内に、例えば100個の関数があったとします。
その中の、ある1つの関数(仮にこの関数をhoge()とする)の実行時間計測を以下の2つの方法で行います。

  1. for文内のhoge()以外の関数を無くした状態で、hoge()のみを時間計測
  2. for文内の全ての関数が有る状態で、hoge()のみを時間計測

1と2の計測結果を比較すると、2の方が1よりも計測時間が長くなります。
ということは、for文内の関数が少ない方がhoge()のみを時間計測しているにも関わらず、hoge()の実行時間が短くなっているということになります。

疑問に思っているのは、なぜこの2つで差が出るかです。
考えられる理由としては、LinuxにおけるTickというものが関係しているのかな、と思っています。
自分が使用しているLinuxのCONFIG_HZは1000でしたので、1[msec]の精度でしか時間計測ができないのだと解釈しています。
なので、時間計測をしても、1[msec]進むごとにCPUが時間をカウントしているので結局それよりも細かな精度では時間計測をする事ができないのだと解釈しています。

ですが、それだけだと1と2でなぜ差ができてしまうのかを理解できません。2の場合、hoge()以外の関数がfor文内に有る事で、hoge()の実行が待たされていて、その待たされた分だけ実行時間が伸びたりしているのでしょうか?

以上、よろしくお願い致します。

main() // 1の時の例
{
uint64_t nsec, sec;
struct timespec start, end;
int ERROR=0;
int ii;
for(ii=0; ii<1000000; ii++){
clock_gettime(CLOCK_MONOTONIC, &start);
hoge();
ERROR = clock_gettime(CLOCK_MONOTONIC, &end);
if(ERROR==0){
if((end.tv_nsec - start.tv_nsec) < 0){
end.tv_nsec += 1000000000;
end.tv_sec  -= 1;
}
nsec = (end.tv_nsec - start.tv_nsec)/1000;   // microsec
sec  = (end.tv_sec - start.tv_sec)*1000000; // microsec
}else{
nsec = 0;
sec  = 0;
}
ofs << sec << "¥t" << nsec << endl;
}

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

こんにちは。

例えば、hogeが0.5mSec程度かかる時、他の関数が無い場合、計時タイマは2回に1回カウントアップします。
他の関数がもし、0.5mSec程度かかっていたら、計時タイマは毎回カウントアップします。
そのような現象が発生しているということはないでしょうか?

hogeの処理時間はどれくらいなのでしょうか?
その時間に対して十分な精度のタイマを用いないと計測は困難と思います。
もしかすると、forループ内に、例えば0mSec以上1.0mSec未満の期間で一様に分布するようなsleep()を入れることで多少は精度改善できるかも知れません。
しかし、hogeだけを十分な回数繰り返し、そのループ全体の処理時間を計測する方が確実です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/04 22:21

    回答ありがとうございます。

    hogeの処理時間は、現状測れている限りでは一回500[nanoSec]ほどです。
    そもそも、この処理時間に対して一回ずつ測るということが困難なのでしょうか?

    キャンセル

  • 2017/01/04 22:42

    500nSecの時間を1mSec精度のタイマで計測するのは無理と思います。
    2000回に1回程度しか1mSec程度のタイマは更新されない筈ですので。

    ただ、CLOCK_MONOTONICの精度は軽く調べてみた範囲では分かりませんでした。
    CONFIG_HZはタイマ割り込み間隔なので、これとは異なる精度かも知れません。

    キャンセル

  • 2017/01/04 23:07

    2000回に1回程度しかタイマが更新されないのですね。。。
    調べて下さり、ありがとうございます。


    それでは、質問をしていた、1と2の差についてはどういった理解をするのが正解なのでしょうか?
    1と2に差が出るのは、計時タイマというものが他の関数がある事によって、カウントアップされてしまい、hoge()の前後で同じように計測しているように思えても、計時タイマのカウントアップの回数が、実は裏では関数のあるなしによって変化しており、それによって差が出てしまう。

    こういった解釈でよいのでしょうか?

    よろしくお願い致します。。。

    キャンセル

  • 2017/01/05 00:02

    > 2000回に1回程度しかタイマが更新されないのですね。。。
    > 調べて下さり、ありがとうございます。

    いえ、これは調べた結果ではないです。CLOCK_MONOTONICの精度が1mSecでhoge()の処理時間が500nSecくらいの場合、1,000,000/500=2,000ですから、2,000回に1回と言う単純計算です。

    > それでは、質問をしていた、1と2の差についてはどういった理解をするのが正解なのでしょうか?

    精度不足のタイマで処理時間を計測している場合、追求してもしかたがないと思います。不確定要素が多すぎてなんとも。
    そもそもCLOCK_MONOTONICの精度は本当に1mSecなのでしょうか?
    実は100nSecくらいの精度があり、かつ、hoge()の処理時間が他の処理の有無で実際に変わっている可能性もあるのではないでしょうか?

    (A)hoge()だけのforループ、(B)hoge()以外だけを含むforループ、(C)hoge()とhoge()以外も含むforループについて、それぞれforループ全体の時間を計測することで何か分かると思います。forループ全体が1秒以上かかるなら、1mSec程度の精度で計測しても計測結果の精度は十分でしょう。
    また、この計測はコンパイル時の最適化をON/OFFの両方で計測することをお勧めします。
    少なくとも最適化OFFでは(A)+(B)==(C)になる筈です。ならない場合、hoge()とhoge()以外の処理が独立でないということになりますから、ロジックの影響でhoge()の処理時間が変わった可能性もあります。

    キャンセル

  • 2017/01/05 19:34

    こんばんは。

    単純計算の件、了解しました。

    CLOCK_MONOTONICの精度は、clock_getres()を用いて調べた結果、1[nsec]の精度で計測できるという結果が出てきました。

    また、(A) + (B) == (C) とはなりませんでした。
    左辺の計測結果よりも、140秒程右辺の方が長くなっておりました。。。
    しかし、どう考えてもhoge()は他の関数から独立しています。。。
    なぜなのか全くわかりません。。

    ちなみに最適化については、コンパイル時に-o0 とする事でオフになると思いますが合っているでしょうか?

    キャンセル

  • 2017/01/05 21:30

    > 最適化については、コンパイル時に-o0 とする事でオフになると思いますが合っているでしょうか?

    合ってます。

    > 左辺の計測結果よりも、140秒程右辺の方が長くなっておりました。。。

    (C)は1ループ当たり140uSec程(A)+(B)より時間がかかるのですね。不思議ですね。
    ところで、(A), (B), (C)の絶対時間はどの程度だったのでしょうか?

    (A)は500nSec * 1,000,000で概ね0.5秒程の筈ですね。(ループ処理でもう少し長い筈ですが。)
    (B)が150秒だった場合、(A)+(B)=約150秒に対して(C)が290秒なら、もしかするとsharowさんが書かれているキャッシュのヒット・ミスの可能性は出てくると思います。

    (A)と(B)がアクセスするメモリが全てキャッシュに入り、(C)ではキャッシュに入りきれないような場合はありえるかも知れません。(私も実際にそのようなケースを目の当たりにしたことはないので、詳しいことは分かりませんが。)

    キャンセル

  • 2017/01/09 21:54

    回答が遅れてしまい申し訳ないです。

    本題の件ですが、自分のミスでございました。。。。
    hoge以外の関数が、hogeに依存している部分がありました。。。

    ここまで親身に教えていただいたにもかかわらず、このような結果となってしまい、申し訳ないです。
    まだ分からないことが多々ありますので、ご教授いただければ幸いです。

    本当にありがとうございました。

    キャンセル

  • 2017/01/09 22:22

    いいえ、よくあることの一つです。
    こちらこそ、よろしく。

    キャンセル

0

自分が使用しているLinuxのCONFIG_HZは1000でしたので、1[msec]の精度でしか時間計測ができないのだと解釈しています。

それはタイマー割り込みの頻度です。

clock_gettime()の精度はclock_getres()で取得できます。

#define _GNU_SOURCE

#include <stdio.h>
#include <time.h>

int main(void)
{
    struct timespec ts;
    clock_getres(CLOCK_MONOTONIC, &ts);
    printf("tv_sec: %lld\n", (long long)ts.tv_sec);
    printf("tv_nsec: %lld\n", (long long)ts.tv_nsec);
}

私の環境では1ns(1ナノ秒)でした。環境や設定(clocksource)で異なるのでご自身の環境で確認してみてください。Intel系でTSCを使っていればみんな10ns以下だとは思います。


本題の方ですが、hoge()以外の関数がたくさんあることでCPUのキャッシュのヒット率には多少の差がでると思われます。linuxのPerfはハードウェアが対応していればキャッシュのヒット率なども取得できるのでこれらの情報も何かヒントになるかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/05 19:49

    回答ありがとうございます。

    本日確認したところ、自分の環境でも1[nsec]の精度であると出ました。

    また、キャッシュ情報ですが、
    1.の時
    8226263758 cycles
    6293569532 instructions
    70999935 cache-references
    4355879 cache-misses
    3.103704084 seconds time elapsed
    0.765 IPC

    2.の時
    278614996415 cycles
    437980263462 instructions
    184177558 cache-references
    24264648 cache-misses
    104.866233353 seconds time elapsed
    1.572 IPC

    となりました。

    キャンセル

  • 2017/01/06 00:36

    情報ありがとうございます。

    総実行命令に占めるキャッシュミスの割合の差が1と2で10倍以上ありますね。それによってでしょうか、1のIPC(Instruction per cycle)である0.765は私の感覚では高すぎる気がします。

    つまり・・これは私の印象ですが、2が遅くて1が速いのではなく、2は普通で1が理想に近い実行効率を出してる感じがします。その理由をキャッシュだけに求めるのは無理があるかもしれませんが、頂いたデータからは1要因として考えられるかもしれません。

    キャンセル

  • 2017/01/09 21:57

    新しい側面からの情報を与えてくださり、参考になりました。

    ありがとうございました。

    キャンセル

0

パイプライン処理・分岐予測・投機実行・メモリキャッシュ・コード最適化技術
このあたりをキーワードについて勉強してみることをおすすめします。

この分野は、アセンブラ言語どころかCPU内部のマイクロコードレベルまで突き詰めなければ具体的な話が
できない実に奥が深い世界なのですが、とりあえずこのあたりの技術概要を知れば、程度問題はさておき
1より2が遅いのが普通だし、A+B=Cとは限らないことも当たり前だと思えるようになると思います。

なお、Z80だとかMC68000だとかのパイプラインもメモリキャッシュもない古いシステムで、コンパイラ
による最適化もなしでやれば1=2かつA+B=Cになるはずです。古いCPU(の載った環境)のエミュレータを
探して、処理にかかったクロックカウント(実行時間だと、エミュレータが動作クロックまでを
エミュレートしていない限り、エミュレータを動かす側で1クロックあたりの時間が変動するため)を
比較できればいいのですが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/09 21:55

    非常に参考になりました。
    勉強させていただきます。

    ありがとうございました。

    キャンセル

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

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Linux

    4030questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。