2

他の実行可能ファイルをテストするために使用されるいくつかのコードに取り組んでいます。便宜上、コードをテスター、テスト対象のコードをクライアントと呼びます。テスターはクライアントを生成し、コマンドをクライアントの stdin に送信し、クライアントの stdout から結果を受け取ります。

最初にいくつかのパフォーマンス テストを行いたかったので、非常に単純なサンプル テスターとクライアントを作成しました。テスターは、クライアントが標準出力に「READY」を書き込むのを待ち、応答としてクライアントの標準入力に「GO」を送信します。次にクライアントは、コマンド ライン フラグで設定されたバイト数を stdout に書き込み、"\nREADY\n" を書き込みます。この時点で、テスターは再び "GO" を書き込みます。これを 10,000 回繰り返した後、テストを完了するのにかかった時間と「スループット」を計算します。10,000 を完了までの時間で割ったものです。

クライアントが「READY」を送信する前に、0、10、100、1000、10000、および 100000 バイトのデータを送信する上記のテストを実行しました。各バイト サイズについて、テストを 10 回繰り返し、平均をとった。ラップトップで Ubuntu VMWare インスタンスを実行すると、1 秒あたり約 10 万 GO/READY ペアのスループットが得られました。パフォーマンスはかなり安定しており、クライアントがテスターに​​送信するバイナリ バイト数にはほとんど依存しませんでした。次に、CentOS を実行する非常に高速な 24 コア サーバーでテストを繰り返しました。ペイロードが 0 バイトの場合、1 秒あたり約 55,000 の GO/READY ペアしか観察できず、クライアントが送信したバイト数が増加するにつれて、パフォーマンスが著しく低下しました。クライアントが "GO" と "READY" の間に 100k バイトを送信した場合、スループットは 1 秒あたり約 6k 操作にすぎませんでした。

そこで3つ質問があります

  1. より高速なマシンでは、同じコードの実行速度が大幅に低下するのはなぜですか
  2. 仮想マシンのパフォーマンスはペイロード サイズに依存しないのに、高速サーバーのパフォーマンスはペイロード サイズに大きく依存するのはなぜですか?
  3. サーバー上で物事を高速化するためにできることはありますか

考えられる理由の 1 つは、高速サーバーでコードを再コンパイルし、別のバージョンの C++ ライブラリを使用しているということです。VMWare マシンは Ubuntu 11.10 を実行しており、高速サーバーは CentOS 6 を実行しています。どちらも 64 ビット マシンです。

関連するテスター コードは次のとおりです。

ios_base::sync_with_stdio(false);
const int BUFFER_SIZE = 2 << 20;
char buffer[BUFFER_SIZE];
process_stdout->rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
Timer timer;
// Wait until the process is ready
string line;
line.reserve(2 << 20);
getline(*process_stdout, line);
CHECK(line == "READY");
timer.Start();
for (int i = 0; i < num_trials; ++i) {
  *process_stdin << "GO\n";
  process_stdin->flush();
  line = "";
  while (line != "READY") {
    getline(*process_stdout, line);
  }
}
double elapsed = timer.Elapsed();
cout << "Done. Did " << num_trials << " iterations in "
     << elapsed << " seconds. Throughput: "
     << double(num_trials) / elapsed << " per second." << endl;

また、(unistd.h からの) read() 呼び出しを使用して 1MB バッファーを呼び出し、memchr を呼び出して "\n" 文字を見つけて READY を探すバージョンも試しましたが、同じパフォーマンス結果が得られました。

関連するクライアント コードは次のとおりです。

// Create a vector of binary data. Some portion of the data will be sent 
// to stdout each time a "GO" is received before sending "READY"
vector<char> byte_source;
const int MAX_BYTES = 1 << 20;
for (int i = 0; i < MAX_BYTES; ++i) {
 byte_source.push_back(i % 256);
}

cout << "READY" << endl;
while (cin.good()) {
  string line;
  getline(cin, line);
  if (line == "GO") {
    // The value of response_bytes comes from a command line flag
    OutputData(response_bytes, byte_source);
    cout << "READY" << endl;
  }
}

// write bytes worth of data from byte_source to stdout
void OutputData(unsigned int bytes,
                const vector<char>& byte_source) {
  if (bytes == 0) {
    return;
  }
  cout.write(&byte_source[0], bytes);
  cout << "\n";
}

どんな助けでも大歓迎です!

4

1 に答える 1

0

VM の速度がペイロード サイズとは無関係であるという事実は、何か間違ったことをしていることを示しています。これらは完全なプログラムではないため、特定するのは困難です。strace を使用して、何が起こっているかを確認します。つまり、クライアントが実際にすべてのデータを送信しているかどうかを確認します (また、テスターが受信すべきすべてのデータを受信して​​いるかどうかも確認します)。

100k READY/GO ペアは多すぎます。基本的に、他に何もせずに、1 秒あたりのコンテキスト スイッチ数の上限に近づいています。

于 2012-06-01T18:39:46.227 に答える