2017-04-19 7 views
1

コンテキストスイッチを測定するプログラムを作成しようとしています。私はこれをIntel's manual rdtsc + rdtscp命令について行っています。コンテキストスイッチでrdtsc + rdtscpを使用する

ここで、これらのタイムスタンプ命令をコンテキストスイッチで使用したいと考えています。私は以下のような一般的なスケルトンを持っています:

// init two pipes P1, P2 
fork(); 

set_affinity();    // same core 

// parent's code: 
    cpuid + rdtsc   // start timer 
    write(timer to P1); 

    read(timer from P2);  // blocks parent if timer value not written 
    rdtscp + cpuid   // stop timer, get difference 

// child's code: 
    read(timer from P1);  // blocks child if timer value not written 
    rdtscp + cpuid   // stop timer, get difference 

    cpuid + rdtsc   // start timer 
    write(timer to P2); 

私はこのコードでいくつかの問題があります。

OSが、子プロセスまたは親プロセスではなく、全く異なるプロセスへのコンテキスト切り替えを選択すると、機能しません。

このコードには、read()システムコールとwrite()システムコールの実行時間も含まれます。

これらの問題を無視して、rdtsc + rdtscp命令を有効に使用していますか?

I know writing a kernel module and disabling preemption/interrupts is a better way 

答えて

1

私は以前これを行っており、コンテキスト切り替え時間を測定する有効な方法と思われます。このような細かいタイミングを取るときはいつも、予期せぬスケジューリングが常に起こります。通常、何千もの時間を測定し、最小値、メディア、平均時間間隔などの数値を調べることで、それに対処します。両方のプロセスをリアルタイムのSCHED_FIFO優先度で実行することで、スケジューリングを問題の少ないものにすることができます。実際にの時間(単一のCPUコア)を知りたければ、両方のプロセスをアフィニティ設定の単一のCPUにバインドする必要があります。あるプロセスが別のプロセスの出力に応答できるレイテンシを知りたければ、それらを異なるCPU上で実行させることは問題ありません。

覚えておいていただきたいもう一つの問題は、自発的および非自発的なコンテキストスイッチ、およびユーザスペースからカーネルスペースから開始するスイッチが異なるコストを持つことです。あなたのものは任意である可能性があります。自発的な測定は難しく、ビジーなループなどから共有メモリを突き止める必要があります。

+0

私は不本意/自主スイッチをチェックするためのコマンド-V' '時間でそれを実行しています。これまでのところ、不随意スイッチは非常に小さい(<0.1%)。コンテキスト切り替え時間は約30Kサイクル/ 12.5usecになります。 –

0

親ループが1000000回あり、ループ全体が親と子の両方にあることを除いて、同様のタイミングコードを使用しました。コードが添付されています。それから、私は擬似コードのように、個々のコンテキストスイッチに時間を合わせるように修正し、1000000回の個別時間を合計し、元のコードとよく一致しました。すでに言及した警告を考えればどちらの方法もうまくいくようです。

興味深いのは、sched_setaffinity()を使用して別のCPU上で実行するように設定すると、コンテキスト切り替え時間が2倍以上になることです。それはなぜそのような時間に影響を与えますか?同じCPUで実行されているプロセス間でパイプが高速になっていますか?

rdtscp.h:

static inline unsigned long rdtscp_start(void) { 
    unsigned long var; 
    unsigned int hi, lo; 

    __asm volatile ("cpuid\n\t" 
      "rdtsc\n\t" : "=a" (lo), "=d" (hi) 
      :: "%rbx", "%rcx"); 

    var = ((unsigned long)hi << 32) | lo; 
    return (var); 
} 

static inline unsigned long rdtscp_end(void) { 
    unsigned long var; 
    unsigned int hi, lo; 

    __asm volatile ("rdtscp\n\t" 
      "mov %%edx, %1\n\t" 
      "mov %%eax, %0\n\t" 
      "cpuid\n\t" : "=r" (lo), "=r" (hi) 
      :: "%rax", "%rbx", "%rcx", "%rdx"); 

    var = ((unsigned long)hi << 32) | lo; 
    return (var); 
    } 

/*see https://www.intel.com/content/www/us/en/embedded/training/ia-32-ia-64-benchmark-code-execution-paper.html 
*/ 

cntxtSwtchr.c:

#define _GNU_SOURCE 
#include <sched.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include "rdtscp.h" 

int main() { 
    int pipe1[2], pipe2[2]; 
    pipe(pipe1) || pipe(pipe2); 
    cpu_set_t set; 
    CPU_ZERO(&set); 

    clock_t tick, tock; 

    int fork_rtn; 
    if ((fork_rtn = fork()) < 0) 
    exit(1); 

    if (fork_rtn == 0) { // Child 
    close(pipe1[1]); 
    close(pipe2[0]); 

    CPU_SET(1, &set); 
    sched_setaffinity(0, sizeof(set), &set); 

    tick = clock(); 
    unsigned long tsc_start = rdtscp_start(); 
    int i; 
    while (read(pipe1[0], &i, 4)) 
     write(pipe2[1], &i, 4); 
    printf("child tsc_ticks: %lu\n", rdtscp_end() - tsc_start); 
    tock = clock(); 
    clock_t ticks = tock - tick; 
    double dt = (double)ticks/CLOCKS_PER_SEC; 
    printf("Elapsed child cpu time: %gs.\n", dt); 

    close(pipe1[0]); 
    close(pipe2[1]); 
    exit(0); 

    } else {    // Parent 
    close(pipe1[0]); 
    close(pipe2[1]); 

    CPU_SET(1, &set); 
    sched_setaffinity(0, sizeof(set), &set); 

    int idx, lim = 1000000; 
    int i_rtnd; 
    tick = clock(); 
    unsigned long tsc_start = rdtscp_start(); 
    for (idx = 0; idx < lim; ++idx) { 
     write(pipe1[1], &idx, 4); 
     read(pipe2[0], &i_rtnd, 4); 
     if (i_rtnd != idx) 
    break; 
    } 
    printf("parent tsc_ticks: %lu\n", rdtscp_end() - tsc_start); 
    tock = clock(); 
    clock_t ticks = tock - tick; 
    double dt = (double)ticks/CLOCKS_PER_SEC; 
    printf("Elapsed parent cpu time: %gs, %gs/switch.\n", dt, dt/lim); 
    if (idx == lim) 
     printf("Parent reached end of processing loop.\n"); 
    else 
     printf("Parent failed to reach end of processing loop.\n"); 

    close(pipe1[1]); 
    close(pipe2[0]); 
    exit(0); 
    } 

} 
関連する問題