32

I run through millions of records and sometimes I have to debug using Console.WriteLine to see what is going on.

However, Console.WriteLine is very slow, considerably slower than writing to a file.

BUT it is very convenient - does anyone know of a way to speed it up?

Oleks
  • 31,955
  • 11
  • 77
  • 132
ManInMoon
  • 6,795
  • 15
  • 70
  • 133

8 Answers8

14

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.");
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Filip Ekberg
  • 36,033
  • 20
  • 126
  • 183
12

You can use the OutputDebugString API function to send a string to the debugger. It doesn't wait for anything to redraw and this is probably the fastest thing you can get without digging into the low-level stuff too much. The text you give to this function will go into Visual Studio Output window.

[DllImport("kernel32.dll")]
static extern void OutputDebugString(string lpOutputString);

Then you just call OutputDebugString("Hello world!");

Oleg Tarasov
  • 1,244
  • 10
  • 13
  • And even though the Ouput screen needs to redraw - you belive this is faster than Console.WriteLine which writes to the same place? – ManInMoon Mar 11 '11 at 11:52
  • 1
    The Pretender - just tested this - BUT it hasn't written to Output window - could it be somewhere else? – ManInMoon Mar 11 '11 at 11:59
  • As far as I understand, your problem is that Console.WriteLine takes too much time to execute and blocks your program flow until it's finished. `OutputDebugString` will return immediately, whether the Output window has redrawn itself or not. If you are concerned with time between the function call and the time when you actually see the message, that's another story. – Oleg Tarasov Mar 11 '11 at 12:01
  • But where does OutputDebugString output to? – ManInMoon Mar 11 '11 at 12:05
  • It seems that there's a problem with this method when it's used under .NET. When this function is called from native C++ application, its output goes straight into VS Output window, but when the same function is called from a managed program, the message gets lost somewhere. Sorry for misleading you, I'll try to look into this. – Oleg Tarasov Mar 11 '11 at 12:43
  • 1
    You can also use DebugView (http://technet.microsoft.com/en-us/sysinternals/bb896647) то view these messages. I've just tested it with a sample .NET 4 console app and it works like a charm. – Oleg Tarasov Mar 11 '11 at 12:45
  • Yes - Itried that - but no messages. Do you have to flag soemthing in .NET - Debugmode??? – ManInMoon Mar 11 '11 at 13:43
  • OK, this is just weird. I've tried the exact same code at home, and DebugView ends up empty. But then I checked the “Enable unmanaged debugging” at “Debug” tab in my project preferences and `OutputDebugString` started to write to the VS Output window oO I suspect this is somehow related to the fact that I had multiple instances of VS opened at work. So if you enable that checkbox in project properties and run a single VS instance you should probably see your messages in the Output window. – Oleg Tarasov Mar 11 '11 at 15:34
  • Why not just use Debug.WriteLine()? However, it's also interesting for me, how refreshing a console window can be slower than redrawing a UI window (VS's Output). Or I've misunderstood smthg?Sorry for misleading – illegal-immigrant Sep 27 '11 at 20:58
10

Do something like this:

public static class QueuedConsole
{
    private static StringBuilder _sb = new StringBuilder();
    private static int _lineCount;

    public void WriteLine(string message)
    {
        _sb.AppendLine(message);
        ++_lineCount;
        if (_lineCount >= 10)
           WriteAll();
    }

    public void WriteAll()
    {
        Console.WriteLine(_sb.ToString());
        _lineCount = 0;
        _sb.Clear();
    }
}

QueuedConsole.WriteLine("This message will not be written directly, but with nine other entries to increase performance.");

//after your operations, end with write all to get the last lines.
QueuedConsole.WriteAll();

Here is another example: Does Console.WriteLine block?

Community
  • 1
  • 1
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Thanks for that. I have a fast writer that uses a file. I am just wondering why such a useful facility is slow - and forces us to come up with creative solutions... – ManInMoon Mar 11 '11 at 11:33
  • Check the other answer that I added as a link. – jgauffin Mar 11 '11 at 11:35
  • 2
    Might be better to create a class then the destructor can print the remaining buffer if the prog. crashes. –  Mar 11 '11 at 11:36
  • Are you sure that the console is still intact if an application crashes? – jgauffin Mar 11 '11 at 11:37
  • I was thinking of exceptions that may be caught at a higher level rather than anything catastrophic. –  Mar 11 '11 at 11:39
  • jgauffin, you've looked at this quite deeply. Do you know why having the output closed whilst running DOES NOT speed things up? – ManInMoon Mar 11 '11 at 11:47
9

I recently did a benchmark battery for this on .NET 4.8. The tests included many of the proposals mentioned on this page, including Async and blocking variants of both BCL and custom code, and then most of those both with and without dedicated threading, and finally scaled across power-of-2 buffer sizes.

The fastest method, now used in my own projects, buffers 64K of wide (Unicode) characters at a time from .NET directly to the Win32 function WriteConsoleW without copying or even hard-pinning. Remainders larger than 64K, after filling and flushing one buffer, are also sent directly, and in-situ as well. The approach deliberately bypasses the Stream/TextWriter paradigm so it can (obviously enough) provide .NET text that is already Unicode to a (native) Unicode API without all the superfluous memory copying/shuffling and byte[] array allocations required for first "decoding" to a byte stream.

If there is interest (perhaps because the buffering logic is slightly intricate), I can provide the source for the above; it's only about 80 lines. However, my tests determined that there's a simpler way to get nearly the same performance, and since it doesn't require any Win32 calls, I'll show this latter technique instead.

The following is way faster than Console.Write:

public static class FastConsole
{
    static readonly BufferedStream str;

    static FastConsole()
    {
        Console.OutputEncoding = Encoding.Unicode;  // crucial

        // avoid special "ShadowBuffer" for hard-coded size 0x14000 in 'BufferedStream' 
        str = new BufferedStream(Console.OpenStandardOutput(), 0x15000);
    }

    public static void WriteLine(String s) => Write(s + "\r\n");

    public static void Write(String s)
    {
        // avoid endless 'GetByteCount' dithering in 'Encoding.Unicode.GetBytes(s)'
        var rgb = new byte[s.Length << 1];
        Encoding.Unicode.GetBytes(s, 0, s.Length, rgb, 0);

        lock (str)   // (optional, can omit if appropriate)
            str.Write(rgb, 0, rgb.Length);
    }

    public static void Flush() { lock (str) str.Flush(); }
};

Note that this is a buffered writer, so you must call Flush() when you have no more text to write.

I should also mention that, as shown, technically this code assumes 16-bit Unicode (UCS-2, as opposed to UTF-16) and thus won't properly handle 4-byte escape surrogates for characters beyond the Basic Multilingual Plane. The point hardly seems important given the more extreme limitations on console text display in general, but could perhaps still matter for piping/redirection.

Usage:

FastConsole.WriteLine("hello world.");
// etc...
FastConsole.Flush();

On my machine, this gets about 77,000 lines/second (mixed-length) versus only 5,200 lines/sec under identical conditions for normal Console.WriteLine. That's a factor of almost 15x speedup.

These are controlled comparison results only; note that absolute measurements of console output performance are highly variable, depending on the console window settings and runtime conditions, including size, layout, fonts, DWM clipping, etc.

Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
  • 1
    I tried calling `FastConsole.Flush` after each `FastConsole.WriteLine`, and the performance became almost identical with the native `Console.WriteLine`. I guess this was expected. The performance boost comes basically from omitting flushing the stream after each write. – Theodor Zoulias Sep 07 '19 at 21:30
6

Why Console is slow:

  • Console output is actually an IO stream that's managed by your operating system. Most IO classes (like FileStream) have async methods but the Console class was never updated so it always blocks the thread when writing.

  • Console.WriteLine is backed by SyncTextWriter which uses a global lock to prevent multiple threads from writing partial lines. This is a major bottleneck that forces all threads to wait for each other to finish the write.

  • If the console window is visible on screen then there can be significant slowdown because the window needs to be redrawn before the console output is considered flushed.

Solutions:

Wrap the Console stream with a StreamWriter and then use async methods:

var sw = new StreamWriter(Console.OpenStandardOutput());
await sw.WriteLineAsync("...");

You can also set a larger buffer if you need to use sync methods. The call will occasionally block when the buffer gets full and is flushed to the stream.

// set a buffer size
var sw = new StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8, 8192);
// this write call will block when buffer is full
sw.Write("...")

If you want the fastest writes though, you'll need to make your own buffer class that writes to memory and flushes to the console asynchronously in the background using a single thread without locking. The new Channel<T> class in .NET Core 2.1 makes this simple and fast. Plenty of other questions showing that code but comment if you need tips.

Mani Gandham
  • 7,688
  • 1
  • 51
  • 60
  • Am I right thinking that it's anyway gonna block a thread, right? Isn't this same as wrap a sync operation into something like `Task.Run` ? I see that `OpenStandardOutput` returns a `Stream` which provides an async API so that seems like I'm wrong and this is indeed asynchronous. – E. Shcherbo Mar 29 '22 at 20:42
  • @E.Shcherbo No. The first option uses standard async methods for writing to a stream, and stdout is just a stream. All you have to do is initialize your own `StreamWriter` instead of using the static `Console` class. This still uses a global lock if you're writing the entire line so there will be contention, which is what I talk about near the end. – Mani Gandham Mar 30 '22 at 22:57
4

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 faster
  • For small bursts of lines (<1000), it's about 10 times faster
  • For larger chunks it seems to converge to about 3x
  • If the Debug output goes to the IDE, Console is faster :-)
  • If DebugView is not running, Debug gets even faster
  • For really large amounts of consecutive outputs (>10000), Debug gets slower and Console stays constant. I presume this is due to the memory, Debug has to allocate and Console does not.
  • Obviously, it makes a difference if DebugView is actually "in-view" or not, as the many gui updates have a significant impact on the overall performance of the system, while Console simply hangs, if visible or not. But it's hard to put numbers on that one...

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})");
     }
   }
  };
 }
}
Xan-Kun Clark-Davis
  • 2,664
  • 2
  • 27
  • 38
1

Try using the System.Diagnostics Debug class? You can accomplish the same things as using Console.WriteLine.

You can view the available class methods here.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
dhirschl
  • 2,088
  • 13
  • 18
  • If you meant `Debug.WriteLine`, it isn't. It seems to be a common misconception. That just wouldn't work unless a PC have an IDE. – Hi-Angel Sep 04 '15 at 11:19
1

Just a little trick I use sometimes: If you remove focus from the Console window by opening another window over it, and leave it until it completes, it won't redraw the window until you refocus, speeding it up significantly. Just make sure you have the buffer set up high enough that you can scroll back through all of the output.

Ian Hughes
  • 401
  • 2
  • 4