Doing some ad hoc benchmarks, I found that my compute/IO intensive program was
significantly slower in .NET than in native C++ (here’s
a more rigorous test showing the same thing). However, when the IO was taken
out (Console.Write), the test showed that the computation in .NET and in native
C++ was nearly the same (with C++ still having a slight edge).
My .NET IO
code looked like this:
static
void OutputIt(int[] vect) {
for (int i = 0; i < vect.Length; ++i) {
if( i > 0 ) Console.Write(”,“);
Console.Write(vect[i]);
}
Console.WriteLine();
}
In tracking down this disparity,
Courteney van den Berg noticed that, unlike STL IO streams in native C++, .NET
Console output is not buffered. A simple manual buffering using a
StringBuilder brings the performance of .NET back in line with native C++:
static
void OutputIt(int[] vect) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < vect.Length; ++i) {
if( i > 0 ) sb.Append(”,“);
sb.Append(vect[i]);
}
Console.WriteLine(sb);
}
Instead of buffering
manually, I could have turned on buffered console IO by reopening console output
with a buffer size:
static
void Main(string[] args) {
using( StreamWriter bufferOutput = new
StreamWriter(Console.OpenStandardOutput(1024)) ) {
Console.SetOut(bufferOutput);
//
Use Console.Write (via OutputIt)…
}
// Remaining buffered Console output flushed
}
//
Console output now buffered
static
void OutputIt(int[] vect) {
for (int i = 0; i < vect.Length; ++i) {
if( i > 0 ) Console.Write(”,“);
Console.Write(vect[i]);
}
Console.WriteLine();
}
Why isn’t Console output buffered by default? Unlike in C++, which has
deterministic finalization, in .NET, console output would have to be manually
flushed or closed (closing causes a flush). Failure to do so can cause console
output to be incomplete at the end of a program. Notice our careful use of the
“using” block in the code above, causing the output stream to be closed at the
end of the block and the remaining buffered output to be flushed, even in the
face of exceptions. Forgetting to use the “using” block or forgetting to
manually flush or close in a finally block, can cause console output to be lost.
Rather than imposing the new burden of manually closing the console output, .NET
architects opted to turn off buffering by default, while still letting you turn
it on at will.