Closed5
WriteLine(int)よりもWriteLine(string, object)の方が速い
以下のコードを比べると、どちらが速いだろうか?
答えは後者
System.Console.WriteLine(1);
System.Console.WriteLine("{0}", 1);
なぜ????
直感的に前者の方が速そうなんだが…
WriteLine(int)の処理を追ってみる
※ソースコードはReferenceSourceで確認できる。
- 内部の
TextWriter
のWriteLine(int)
を呼んでいる
public static void WriteLine(int value)
{
Out.WriteLine(value);
}
-
Write(int)
とWriteLine()
を呼んでいる
WriteLine()
は本筋ではないので無視
public virtual void WriteLine(int value) {
Write(value);
WriteLine();
}
-
int
のToString
を呼んで、それを引数にしてWrite(string)
を呼んでいる
気がかりなのは、FormatProvider
というものを引数にしている点(ひとまず無視)
public virtual void Write(int value) {
Write(value.ToString(FormatProvider));
}
-
Write(char[])
を呼んでいる
public virtual void Write(String value) {
if (value != null) Write(value.ToCharArray());
}
-
Write(char[], int, int)
を呼んでいる
public virtual void Write(char[] buffer) {
if (buffer != null) Write(buffer, 0, buffer.Length);
}
- エラーチェックの後、文字列の長さだけ
Write(char)
を呼んでいる
// Writes a range of a character array to the text stream. This method will
// write count characters of data into this TextWriter from the
// buffer character array starting at position index.
//
public virtual void Write(char[] buffer, int index, int count) {
if (buffer==null)
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
if (index < 0)
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - index < count)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
Contract.EndContractBlock();
for (int i = 0; i < count; i++) Write(buffer[index + i]);
}
WriteLine(string, object)の処理を追ってみる
- 内部の
TextWriter
のWriteLine(string, object)
を呼んでいる
public static void WriteLine(String format, Object arg0)
{
Out.WriteLine(format, arg0);
}
-
String.Format
でstring
にしたものを引数にしてWriteLine(string)
を呼んでいる
この時点でWriteLine(int)
の場合はWrite(string)
とWriteLine()
に分けていたのに、直接WriteLine(string)
を呼んでいる!?
public virtual void WriteLine(String format, Object arg0)
{
WriteLine(String.Format(FormatProvider, format, arg0));
}
-
WriteLine(string)
では、単にWrite(string)
とWriteLine()
を呼ぶのではなく、何かしているぞ🤔
// Writes a string followed by a line terminator to the text stream.
//
public virtual void WriteLine(String value) {
if (value==null) {
WriteLine();
}
else {
// We'd ideally like WriteLine to be atomic, in that one call
// to WriteLine equals one call to the OS (ie, so writing to
// console while simultaneously calling printf will guarantee we
// write out a string and new line chars, without any interference).
// Additionally, we need to call ToCharArray on Strings anyways,
// so allocating a char[] here isn't any worse than what we were
// doing anyways. We do reduce the number of calls to the
// backing store this way, potentially.
int vLen = value.Length;
int nlLen = CoreNewLine.Length;
char[] chars = new char[vLen+nlLen];
value.CopyTo(0, chars, 0, vLen);
// CoreNewLine will almost always be 2 chars, and possibly 1.
if (nlLen == 2) {
chars[vLen] = CoreNewLine[0];
chars[vLen+1] = CoreNewLine[1];
}
else if (nlLen == 1)
chars[vLen] = CoreNewLine[0];
else
Buffer.InternalBlockCopy(CoreNewLine, 0, chars, vLen * 2, nlLen * 2);
Write(chars, 0, vLen + nlLen);
}
/*
Write(value); // We could call Write(String) on StreamWriter...
WriteLine();
*/
}
英訳
WriteLineはアトミックであることが理想で、WriteLineの1回の呼び出しはOSの1回の呼び出しに等しくなります(つまり、コンソールに書き込みながら同時にprintfを呼び出せば、干渉なしに文字列と改行文字を書き出すことが保証されます)。
さらに、Stringに対してToCharArrayを呼び出す必要があるので、ここでchar[]を確保することは、これまでやっていたことよりも悪いことではありません。 この方法で、バッキングストアの呼び出し回数を減らせる可能性があります。
バッキングストアの呼び出しが1回になるってことかな??
String.Format
のコストよりもOSの標準出力の呼び出しコストの方が大きいから、速くなるんだろうなぁ
因みに改行なしのWrite(int)
とWrite(string, object)
だと前者の方が速い。
Stopwatch
を使った計測
for (int i = 0; i < N; i++) {
Console.WriteLine(1);
}
N=100000000 | |
---|---|
WriteLine(1.ToString()) |
4.079 sec |
WriteLine(1) |
5.902 sec |
まとめ
Out.WriteLine(String)
を呼ぶようにした方が速くなる。
このスクラップは2023/03/15にクローズされました