
そこで、C++ で書かれたプログラムがあります。
すべての計算にかかった時間を教えてくれますし、かなり負荷の高いマルチスレッド計算も大量に実行します。
まったく同じマシンでプログラムを実行すると、TTY から起動した場合はすべての計算に約 20 ~ 21 秒かかりますが、GNOME ターミナルから起動した場合は約 0.2 秒しかかからないことに気付きました。
何が原因でしょうか? 文字通り、同じマシン上のまったく同じファイルです。
答え1
背景理論
CTRLそうですね、 ++と GNOME ターミナルで作業するものは、どちらもALT同じF1概念の異なる実装です。模倣するいわゆるフルスクリーンターミナル。
前者は Linux では仮想端末 (VT) と呼ばれ、通常は単に「コンソール」と呼ばれます。これは、x86 互換プラットフォーム (つまり、「IBM PC」の伝統) のハードウェア ビデオ カードによって現在も提供されている特別な「テキストのみ」のビデオ モードを使用します。後者は GUI アプリケーションです。
どちらも、その助けを借りて実行されるアプリケーションに、そのアプリケーションが「端末デバイス」に期待する一連の機能を提供します(詳細とさらなる指針—ここ)。
目下の課題
さて、次は知覚される遅さについて考えてみましょう。
問題の核心は、プログラムがいわゆる「ブロッキング」I/Oを行っていることだと思います。つまり、次のようなことを行うたびに、
std::cout << "Hello, world" << endl;
コード内では、まずアプリケーションにリンクされたC++標準ライブラリのコードが起動し、指定された宛先に送信されたものの出力を処理します。ストリーム。
特定の処理(通常はバッファリング)の後、このデータは実際にプログラムの実行プロセスから出て、プログラムが出力を送信するメディアに実際に出力される必要があります。Linux(およびその他のUnix互換システム)では、専用のシステムコール(またはシステムコール略して といいますwrite()
。
したがって、C++ stdlib は最終的にそのwrite()
システムコールを実行し、それが完了するまで待機します。つまり、カーネルが「OK、データの受信者がデータを取得したことを通知しました」と応答するのを待機します。
推測できるように、プログラムが出力するデータの受信者は、プログラムを実行している端末 (エミュレーター) です。つまり、テストでは Linux VT または GNOME ターミナルのインスタンスのいずれかです。(カーネルは実行中の端末エミュレーターにデータを直接送信しないため、全体像はより複雑ですが、説明を複雑にしないことにします。)
したがって、そのシステムコールが完了する速度は、write()
データの受信者がそれを処理する速度に大きく依存します。あなたの場合、GNOME ターミナルの方がはるかに高速です。
違いについての私の見解は、VT ドライバーは送られてくるすべてのデータを忠実にレンダリングし、スクロールするなどするのに対し、GNOME ターミナルは、受信データのバーストを最適化して、その末尾部分 (ターミナルの画面サイズに合うもの) のみをレンダリングし、残りをほとんどの GUI ターミナル エミュレーターが備えている、いわゆる「スクロール バッファー」に配置するという点です。
やるべきこと
ここで覚えておくべき重要なことは、プログラムが計算とともに I/O を実行し、「ウォールクロック」タイマーを使用してプログラムの計算速度を測定すると、通常は計算速度ではなく I/O 速度を測定できるということです。
I/Oは扱いが難しいことに注意してください。プロセスは先取りされたハードディスク ドライブなどの I/O リソースが書き込みに使用できるようになるまで待機するたびに、OS によってプロセスが停止 (リソースが別のプロセスに引き渡されて停止) されます。
したがって、計算の「生の」パフォーマンスを測定する確実な方法は、プログラムにすべてのI/Oを無効にする機能を持たせることです。それが不可能であったり、実装するのが面倒だったりする場合は、少なくともすべての出力をいわゆる「ヌルデバイス」に向けるようにしてください。そのためには/dev/null
、次のようにプログラムを実行します。
$ ./program >/dev/null
ヌル デバイスは、渡されたすべてのデータを単純に破棄します。したがって、C++ stdlib によって実行される各 I/O ラウンドはカーネルにヒットしますが、少なくとも書き込み速度はほぼ一定 (ほぼ瞬時) になります。
両方の対策が必要な場合そして生成されたデータを保存するには、いわゆる RAM ディスクを作成し、そこに保存されているファイルに出力をリダイレクトすることを検討してください。
測定についてもう 1 つ注意すべき点は、一見アイドル状態のシステムでコモディティ OS (Ubuntu など) を実行している場合でも、CPU がスリープ状態になることはなく、バックグラウンドで何らかのタスクが常に実行されているということです。つまり、I/O がない場合でも、または (上で説明したように) ある程度「無効」な I/O がある場合でも、計算パフォーマンスを測定すると、実行ごとに異なる結果が生成されます。
これを補うために、適切なベンチマークとは、同じ入力データを使用して計算を数千回実行し、実行回数にわたって結果を平均化することを意味します。