I run through millions of records and sometimes I have to debug using Console.WriteLine
to see what is going on.
However, Console.WriteLine
A little old thread and maybe not exactly what the OP is looking for, but I ran into the same question recently, when processing audio data in real time.
I compared Console.WriteLine
to Debug.WriteLine
with this code and used DebugView as a dos box alternative. It's only an executable (nothing to install) and can be customized in very neat ways (filters & colors!). It has no problems with tens of thousands of lines and manages the memory quite well (I could not find any kind of leak, even after days of logging).
After doing some testing in different environments (e.g.: virtual machine, IDE, background processes running, etc) I made the following observations:
Debug
is almost always fasterDebug
output goes to the IDE, Console
is faster :-)Debug
gets even fasterDebug
gets slower and Console
stays constant. I presume this is due to the memory, Debug has to allocate and Console
does not.I did not try multiple threads writing to the Console
, as I think this should generally avoided. I never had (performance) problems when writing to Debug
from multiple threads.
If you compile with Release settings, usually all Debug
statements are omitted and Trace
should produce the same behaviour as Debug.
I used VS2017 & .Net 4.6.1
Sorry for so much code, but I had to tweak it quite a lot to actually measure what I wanted to. If you can spot any problems with the code (biases, etc.), please comment. I would love to get more precise data for real life systems.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace Console_vs_Debug {
class Program {
class Trial {
public string name;
public Action console;
public Action debug;
public List < float > consoleMeasuredTimes = new List < float > ();
public List < float > debugMeasuredTimes = new List < float > ();
}
static Stopwatch sw = new Stopwatch();
private static int repeatLoop = 1000;
private static int iterations = 2;
private static int dummy = 0;
static void Main(string[] args) {
if (args.Length == 2) {
repeatLoop = int.Parse(args[0]);
iterations = int.Parse(args[1]);
}
// do some dummy work
for (int i = 0; i < 100; i++) {
Console.WriteLine("-");
Debug.WriteLine("-");
}
for (int i = 0; i < iterations; i++) {
foreach(Trial trial in trials) {
Thread.Sleep(50);
sw.Restart();
for (int r = 0; r < repeatLoop; r++)
trial.console();
sw.Stop();
trial.consoleMeasuredTimes.Add(sw.ElapsedMilliseconds);
Thread.Sleep(1);
sw.Restart();
for (int r = 0; r < repeatLoop; r++)
trial.debug();
sw.Stop();
trial.debugMeasuredTimes.Add(sw.ElapsedMilliseconds);
}
}
Console.WriteLine("---\r\n");
foreach(Trial trial in trials) {
var consoleAverage = trial.consoleMeasuredTimes.Average();
var debugAverage = trial.debugMeasuredTimes.Average();
Console.WriteLine(trial.name);
Console.WriteLine($ " console: {consoleAverage,11:F4}");
Console.WriteLine($ " debug: {debugAverage,11:F4}");
Console.WriteLine($ "{consoleAverage / debugAverage,32:F2} (console/debug)");
Console.WriteLine();
}
Console.WriteLine("all measurements are in milliseconds");
Console.WriteLine("anykey");
Console.ReadKey();
}
private static List < Trial > trials = new List < Trial > {
new Trial {
name = "constant",
console = delegate {
Console.WriteLine("A static and constant string");
},
debug = delegate {
Debug.WriteLine("A static and constant string");
}
},
new Trial {
name = "dynamic",
console = delegate {
Console.WriteLine("A dynamically built string (number " + dummy++ + ")");
},
debug = delegate {
Debug.WriteLine("A dynamically built string (number " + dummy++ + ")");
}
},
new Trial {
name = "interpolated",
console = delegate {
Console.WriteLine($ "An interpolated string (number {dummy++,6})");
},
debug = delegate {
Debug.WriteLine($ "An interpolated string (number {dummy++,6})");
}
}
};
}
}
If it is just for debugging purposes you should use Debug.WriteLine instead. This will most likely be a bit faster than using Console.WriteLine
.
Example
Debug.WriteLine("There was an error processing the data.");
Here is a 7 times faster implementation that bulk-writes to the Console
, with a 10 msec delay. The downside is that you must remember to call Console2.Flush()
at the end of the program, otherwise you may lose some output.
public static class Console2
{
private static readonly StringBuilder _sb = new StringBuilder();
private static volatile CancellationTokenSource _cts;
private static int _count;
public static void Write(string value)
{
lock (_sb) _sb.Append(value);
ScheduleFlush();
}
public static void Write(string format, params object[] args)
{
lock (_sb) _sb.AppendFormat(format, args);
ScheduleFlush();
}
public static void WriteLine(string value)
=> Write(value + Environment.NewLine);
public static void WriteLine(string format, params object[] args)
=> Write(format + Environment.NewLine, args);
public static void WriteLine()
=> WriteLine("");
private static void ScheduleFlush()
{
_cts?.Cancel();
var count = Interlocked.Increment(ref _count);
if (count % 100 == 0) // periodically flush without cancellation
{
var fireAndForget = Task.Run(Flush);
}
else
{
_cts = new CancellationTokenSource();
var token = _cts.Token;
var fireAndForget = Task.Run(async () =>
{
await Task.Delay(10, token);
Flush();
}, token);
}
}
public static void Flush()
{
_cts?.Cancel();
string text;
lock (_sb)
{
if (_sb.Length == 0) return;
text = _sb.ToString();
_sb.Clear();
}
Console.Write(text);
}
}
Usage example:
for (int i = 1; i <= 1000; i++)
{
Console2.WriteLine($"{DateTime.Now:HH:mm:ss.fff} > Line {i}");
}
Console2.Flush();
Output:
06:27:22.882 > Line 1
06:27:22.882 > Line 2
...
06:27:22.893 > Line 999
06:27:22.893 > Line 1000