Console.WriteLine很慢

问题描述 投票:23回答:7

我浏览了数百万条记录,有时我必须使用Console.WriteLine进行调试,看看发生了什么。

但是,Console.WriteLine非常慢,比写入文件慢得多。

但它非常方便 - 有没有人知道加速它的方法?

c# performance
7个回答
7
投票

您可以使用OutputDebugString API函数将字符串发送到调试器。它不会等待任何重绘,这可能是你不能过多地挖掘低级别东西所能获得的最快的东西。您为此函数提供的文本将进入Visual Studio输出窗口。

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

然后你只需要打电话给OutputDebugString("Hello world!");


12
投票

如果它只是为了调试目的,你应该使用Debug.WriteLine。这很可能比使用Console.WriteLine快一点。

Debug.WriteLine("There was an error processing the data.");


7
投票

做这样的事情:

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();

这是另一个例子:Does Console.WriteLine block?


3
投票

一个旧的线程,可能不是OP正在寻找的,但我最近遇到了同样的问题,实时处理音频数据。

我将Console.WriteLineDebug.WriteLinethis code进行了比较,并使用DebugView作为dos box替代品。它只是一个可执行文件(无需安装),可以非常简洁的方式进行自定义(过滤器和颜色!)。它没有成千上万行的问题,并且很好地管理内存(即使经过几天的记录,我也找不到任何泄漏)。

在不同环境中进行一些测试后(例如:虚拟机,IDE,后台进程运行等),我做了以下观察:

  • Debug几乎总是更快
  • 对于小阵线(<1000),它的速度提高了大约10倍
  • 对于较大的块,它似乎收敛到大约3倍
  • 如果Debug输出进入IDE,Console更快:-)
  • 如果DebugView没有运行,Debug会变得更快
  • 对于非常大量的连续输出(> 10000),Debug变慢并且Console保持不变。我认为这是由于内存,Debug必须分配和Console不。
  • 显然,如果DebugView实际上是“在视图中”,它会有所不同,因为许多gui更新对系统的整体性能有重大影响,而Console只是挂起,如果可见或不可见。但很难将数字放在那个......

我没有尝试多个线程写入Console,因为我认为这应该通常避免。从多个线程写入Debug时,我从未遇到(性能)问题。

如果使用Release设置进行编译,通常会省略所有Debug语句,并且Trace应该产生与Debug相同的行为。

我使用VS2017和.Net 4.6.1

很抱歉这么多代码,但我不得不调整它实际测量我想要的东西。如果您发现代码有任何问题(偏见等),请发表评论。我希望能为现实生活系统获得更精确的数据。

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})");
     }
   }
  };
 }
}

1
投票

尝试使用System.Diagnostics Debug类?您可以使用Console.WriteLine完成相同的操作。

您可以在此处查看可用的类方法:http://msdn.microsoft.com/en-us/library/system.diagnostics.debug.aspx


1
投票

为什么控制台很慢:

  1. 控制台输出是由您的操作系统使用和重定向的IO流。大多数IO类(如FileStream)都有异步方法,但Console类从未更新过,它总是阻塞线程。
  2. Console.WriteLineSyncTextWriter支持,它使用全局锁来防止多个线程写入部分行。这是一个主要的瓶颈,使所有线程等待单个阻塞写入调用按顺序发生。
  3. 如果控制台窗口在屏幕上可见,则可能会显着减慢,因为每次更新都需要重新绘制窗口。

解决方案:

使用包装StreamWriter,然后使用异步方法写入Console流:

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

您还可以设置较大的缓冲区以使用同步方法,并在刷新时偶尔进行阻止。

var sw = new StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8, 8192);
sw.Write("...") // this will block for flushing when the buffer size of 8192 is full

如果你想要最快的写入,你需要创建自己的缓冲类,写入内存并使用单个线程在后台异步刷新到控制台而不锁定。 new Channel<T> class in .NET Core 2.1使这简单快速。许多其他问题显示代码,但如果您需要提示,请发表评论。


0
投票

我有时会使用一个小技巧:如果你通过打开另一个窗口从控制台窗口中移除焦点,并将其保留直到完成,它将不会重新绘制窗口,直到你重新聚焦,显着加快速度。只需确保缓冲区设置得足够高,以便可以向后滚动所有输出。

© www.soinside.com 2019 - 2024. All rights reserved.