C++入門の本の第1章では、次のように述べています。
endlはマニピュレータと呼ばれる特別な値であり、出力ストリームに書き込まれると、出力に改行を書き込む効果があり、そのデバイスに関連付けられたバッファをフラッシュする。 バッファをフラッシュすることにより、ユーザーがストリームに書き込まれた出力をすぐに確認できるようにします。
ここで「バッファをフラッシュする」とはどういう意味ですか?
出力は通常、目的のデバイスに書き込まれる前にバッファリングされます。そうすれば、低速でデバイス(ファイルなど)にアクセスするために書き込むときに、1文字ごとにデバイスにアクセスする必要がありません。
フラッシュとは、バッファを空にして実際にデバイスに書き込むことを意味します。
C++のiostreamはバッファリングされます。つまり、ostreamに出力するときに、コンテンツはストリームの背後にあるものにすぐには移動しません。 coutの場合はstdout。ストリームの実装は、ストリームのバッファリングされた部分を実際にいつ送信するかを決定します。これは効率上の理由で行われます。この問題をバッファリングすることにより、ネットワークまたはディスクストリームに1バイトずつ書き込むのは非常に非効率的です。
ただし、これは、たとえばデバッグメッセージをログファイルに書き込んでプログラムがクラッシュすると、ログの一部がまだストリームのバッファに残っている可能性があるため、ストリームを介してログファイルに書き込んだデータの一部が失われる可能性があることを意味します。実際のファイルにはまだ書き込まれていません。これを防ぐには、明示的なflushメソッド呼び出しによって、またはendlの便利な方法を使用して、ストリームにバッファーをフラッシュさせる必要があります。
ただし、定期的にファイルに書き込む場合は、endlの代わりに\ nを使用して、ストリームが行ごとに不必要にストリームをフラッシュしてパフォーマンスが低下するのを防ぐ必要があります。
このメモを含むように編集:
cinとcoutには特別な関係があり、cinから読み取ると、事前にcoutが自動的にフラッシュされます。これにより、たとえばcoutに書き込んだプロンプトは、cinからの読み取りが入力を待機する前に実際にユーザーに表示されます。したがって、coutでも、通常はendlは必要ありませんが、代わりに\ nを使用できます。他のストリーム同士を結びつけることによって、他のストリーム間にもそのような関係を作成できます。
ここで「バッファをフラッシュする」とはどういう意味ですか?
_std::endl
_により、ストリームの内部ステージングメモリ(その「バッファ」)内のデータがオペレーティングシステムに「フラッシュ」(転送)されます。後続の動作は、ストリームがマップされているデバイスのタイプによって異なりますが、一般的に、フラッシュすると、データが物理的に関連するデバイスに転送されたように見えます。しかし、突然の力の喪失は幻想を打ち負かすかもしれません。
このフラッシュにはある程度の オーバーヘッド (無駄な時間)が含まれるため、実行速度が重要な懸念事項である場合は最小限に抑える必要があります。このオーバーヘッドの全体的な影響を最小限に抑えることが データバッファリング の基本的な目的ですが、この目標は過度のフラッシュによって無効になる可能性があります。
コンピューティングシステムのI/Oは通常、非常に洗練されており、複数の抽象化レイヤーで構成されています。このような各レイヤーは、ある程度のオーバーヘッドをもたらす可能性があります。データバッファリングは、システムの2つのレイヤ間で実行される個々のトランザクションの数を最小限に抑えることにより、このオーバーヘッドを削減する方法です。
CPU /メモリシステムレベルのバッファリング(キャッシュ):非常に高いアクティビティでは、コンピューターのランダムアクセスメモリシステムでもボトルネックになる可能性があります。これに対処するために、CPUは隠しキャッシュの複数のレイヤー(キャッシュラインと呼ばれる個々のバッファー)を提供することにより、メモリアクセスを仮想化します。これらのプロセッサキャッシュは、メモリバスでの冗長アクセスを最小限に抑えるために、アルゴリズムのメモリ書き込みをバッファリングします( 書き込みポリシー に準拠)。
アプリケーションレベルのバッファリング:常に必要なわけではありませんが、アプリケーションがメモリのチャンクを割り当てて出力データを蓄積してから渡すことは珍しくありません。 I/Oライブラリに。これは、ランダムアクセスを許可するという基本的な利点を提供します(必要な場合)。ただし、これを行う主な理由は、ライブラリの呼び出しに関連するオーバーヘッドを最小限に抑えることです。これは、単にメモリアレイに書き込むよりもかなり時間がかかる場合があります。
I/Oライブラリバッファリング: C++ IOストリームライブラリ オプションで次のバッファを管理します開いているすべてのストリーム。このバッファは、特に、オペレーティングシステムカーネルへの システムコール の数を制限するために使用されます。自明ではないオーバーヘッドがあります。これは_std::endl
_。を使用するときにフラッシュされるバッファです。
オペレーティングシステムのカーネルおよびデバイスドライバー:オペレーティングシステムは、ストリームが接続されている出力デバイスに基づいて、データを特定のデバイスドライバー(またはサブシステム)にルーティングします。この時点で、実際の動作は、そのタイプのデバイスの性質と特性によって大きく異なる可能性があります。たとえば、デバイスがハードディスクの場合、デバイスドライバーはnotデバイスへの即時転送を開始しますが、冗長性をさらに最小限に抑えるために独自のバッファーを維持します操作(ディスクもチャンクで最も効率的に書き込まれるため)。カーネルレベルのバッファを明示的にフラッシュするために、システムレベルの関数 fsync() on Linux
[closeing ストリームは、必ずしもそのようなフラッシュを強制するわけではありません。
出力デバイスの例には、次のものが含まれます。
ハードウェアバッファ:特定のハードウェアには独自のメモリバッファが含まれている場合があります。たとえば、ハードドライブには通常、システムのCPUをプロセス全体で使用することなく物理書き込みを実行できるようにするために、 ディスクバッファ が含まれています。
多くの状況下では、これらのさまざまなバッファリングレイヤーは(ある程度)冗長になる傾向があり、したがって本質的に過剰です。ただし、各レイヤーでのバッファリングcanは、他のレイヤーが何らかの理由で、各レイヤーに関連するオーバーヘッドに関して最適なバッファリングを提供できない場合、スループットを大幅に向上させます層。
簡単に言うと、_std::endl
_onlyは、その特定のストリームのC++ IOストリームライブラリによって管理されるバッファをアドレス指定しました。 _std::endl
_を呼び出した後、データはカーネルレベルの管理に移動され、データで次に何が起こるかは、非常に多くの要因に依存します。
std::endl
_のオーバーヘッドを回避する方法std::endl
_を使用しない-代わりに_'\n'
_を使用します。std::endl
_を使用しない- 代わりに次のバージョンのようなものを使用する ..._inline std::ostream & endl( std::ostream & os )
{
os.put( os.widen('\n') ); // http://en.cppreference.com/w/cpp/io/manip/endl
if ( debug_mode ) os.flush(); // supply 'debug_mode' however you want
return os;
}
_
この例では、内部コールを呼び出して、または呼び出さずに呼び出すことができるカスタムendl
を提供します flush()
への転送を強制しますオペレーティングシステム)。 (_debug_mode
_変数を使用して)フラッシュを有効にすると、プログラムが終了したときに出力(ディスクファイルなど)を調べてから、関連するストリームを完全に閉じることができるようにするシナリオをデバッグする場合に役立ちます(これにより、バッファーの最終フラッシュを強制しました)。
std::cout
を使用する場合、出力演算子の後に使用されるオペランド(<<
)はバッファーに格納され、それまではstdin
(通常はターミナル、またはコマンドプロンプト)に表示されません。 std::endl
またはstd::cin
に遭遇すると、バッファがflushedになります。つまり、バッファの内容をstdin
に表示/出力します。
このプログラムを考えてみましょう:
#include <iostream>
#include <unistd.h>
int main(void)
{
std::cout << "Hello, world";
sleep(2);
std::cout << std::endl;
return 0;
}
得られる出力は次のようになります。
2秒後
こんにちは世界
C++でのバッファI/Oの効果を示す1つの簡単なコード
指定した入力はすべてバッファリングされ、入力の場合はプログラム変数に渡されます。
以下のコードを見てください:
//program to test how buffered I/O can have unintended effects on our program
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a;
char c;
cin>>a;
cin>>c;
cout<<"the number is : "<<a;
cout<<"\nthe character is : "<<c;
}
ここでは、intとcharの2つの変数を宣言しています。数値を「12d34」と入力すると、int変数は値として12のみを受け入れ、残りはバッファーに残ります。そして、次の入力では、char変数は、入力を要求することなく、値「d」を自動的に受け入れます。