我浏览了数百万条记录,有时我必须使用调试Console.WriteLine
来查看发生了什么.
但是,Console.WriteLine
速度非常慢,比写入文件慢得多.
但它非常方便 - 有没有人知道加速它的方法?
Fil*_*erg 13
如果仅用于调试目的,则应使用Debug.WriteLine
.这很可能比使用快一点Console.WriteLine
.
例
Debug.WriteLine
您可以使用OutputDebugString
API函数将字符串发送到调试器.它不会等待任何重绘,这可能是你不能过多地挖掘低级别东西所能获得的最快的东西.您为此函数提供的文本将进入Visual Studio输出窗口.
[DllImport("kernel32.dll")] static extern void OutputDebugString(string lpOutputString);
然后你就打电话 OutputDebugString("Hello world!");
做这样的事情:
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();
Run Code Online (Sandbox Code Playgroud)
这是另一个例子:Console.WriteLine是否阻止?
为什么控制台很慢:
控制台输出实际上是由操作系统管理的 IO 流。大多数 IO 类(如FileStream
)都有异步方法,但该类Console
从未更新,因此在写入时它总是阻塞线程。
Console.WriteLine
支持SyncTextWriter
使用全局锁来防止多个线程写入部分行。这是一个主要瓶颈,迫使所有线程相互等待对方完成写入。
如果控制台窗口在屏幕上可见,则速度可能会显着减慢,因为在控制台输出被视为刷新之前需要重新绘制窗口。
解决方案:
用 a 包装控制台流StreamWriter
,然后使用异步方法:
var sw = new StreamWriter(Console.OpenStandardOutput());
await sw.WriteLineAsync("...");
Run Code Online (Sandbox Code Playgroud)
如果需要使用同步方法,您还可以设置更大的缓冲区。当缓冲区已满并刷新到流时,调用偶尔会阻塞。
// set a buffer size
var sw = new StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8, 8192);
// this write call will block when buffer is full
sw.Write("...")
Run Code Online (Sandbox Code Playgroud)
如果您想要最快的写入速度,则需要创建自己的缓冲区类,该缓冲区类使用单个线程在后台异步写入内存并刷新到控制台,而无需锁定。.NET Core 2.1 中的新Channel<T>
类使这一切变得简单而快速。很多其他问题显示了该代码,但如果您需要提示,请发表评论。
我最近在.NET 4.8上为此做了一个基准电池。测试包括本页提到的许多提议,包括Async
BCL 和自定义代码的阻塞变体,然后是大多数有和没有专用线程的测试,最后扩展到 2 的幂次缓冲区大小。
最快的方法,现在在我自己的项目中使用,一次将 64K 的宽 ( Unicode ) 字符从.NET缓冲到Win32函数,WriteConsoleW
无需复制甚至硬固定。大于64K的剩余部分,在填充和刷新一个缓冲区后,也直接发送,并且就地发送。该方法故意绕过Stream
/TextWriter
范式,因此它可以(显然足够)将已经是 Unicode 的.NET 文本提供给(本机)Unicode API,而byte[]
无需进行首次“解码”到字节流所需的所有多余的内存复制/改组和数组分配.
如果有兴趣(可能是因为缓冲逻辑有点复杂),我可以提供上面的来源;它只有大约 80 行。但是,我的测试确定有一种更简单的方法可以获得几乎相同的性能,并且由于它不需要任何 Win32 调用,因此我将展示后一种技术。
以下方法比快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(); }
};
Run Code Online (Sandbox Code Playgroud)
请注意,这是一个缓冲写入器,因此您必须Flush()
在没有更多文本可写入时调用。
我还应该提到,如图所示,从技术上讲,此代码假定为 16 位 Unicode(UCS-2,而不是UTF-16),因此无法正确处理Basic Multilingual Plane之外的字符的 4 字节转义代理。考虑到控制台文本显示的更极端限制,这一点似乎并不重要,但对于管道/重定向可能仍然很重要。
用法:
FastConsole.WriteLine("hello world.");
// etc...
FastConsole.Flush();
Run Code Online (Sandbox Code Playgroud)
在我的机器,这得到约77000行/秒(混合长度)相对于仅5200为正常相同的条件下行/秒Console.WriteLine
。这是几乎 15 倍加速的一个因素。
这些只是受控的比较结果;请注意,控制台输出性能的绝对测量值变化很大,这取决于控制台窗口设置和运行时条件,包括大小、布局、字体、DWM 剪辑等。
归档时间: |
|
查看次数: |
18423 次 |
最近记录: |