快速读取控制台输入
Fast reading of console input
我需要从控制台的标准输入流中快速读取数据。输入包含 100.000 行,每行 20 个字符(200 万个字符);用户从剪贴板粘贴它。我的程序运行了大约 3 分钟(非常 很慢;目标是 10 秒)。它看起来像:
var inputData = new string[100000]; // 100.000 rows with 20 chars
for (int i = 0; i < 100000; i++) // Cycle duration is about 3 minutes...
{
inputData[i] = Console.ReadLine();
}
// some processing...
我尝试了什么:
直接:Console.Read,Console.ReadKey-结果一样
Console.In: Read(), ReadLine(), ReadAsync(), ReadLineAsync(), ReadBlock(with各种块大小)、ReadBlockAsync()、ReadToEnd()、ReadToEndAsync() - 相同的结果
new StreamReader(Console.OpenStandardInput(buffer)) 具有不同的缓冲区和块大小 - 相同的结果
开始阅读时隐藏控制台window,阅读结束时显示-加速10%
我尝试从文件中获取输入数据 - 它运行得非常好而且速度很快。但我需要阅读 __ConsoleStream.
我注意到,在进行输入读取时 - 进程 conhost.exe 主动使用处理器。
如何加快输入的读取速度?
更新:
Increasing/decreasing Console.BufferHeight 和 Console.BufferWidth 没有效果
ReadFile
msdn也在慢。但是我注意到一个有趣的事实:
ReadFile(handle, buffer, bufferSize, out bytesCount, null);
// bufferSize may be very big, but buffer obtains no more than one row (with \r\n).
// So, it seems that data passed into InputStream row-by-row syncroniously.
使用本机 WinApi 函数:
WinApi 在 C# 中的使用示例:http://www.pinvoke.net/
你在这里的主要减速是 Console.Read() 和 Console.ReadLine() 都 "echo" 你在屏幕上的文本 - 并且编写文本的过程会减慢你的速度.那么,您要使用的是 Console.Readkey(true),它不会回显粘贴的文本。这是一个在大约 1 秒内写入 100,000 个字符的示例。它可能需要根据您的目的进行一些修改,但我希望这足以为您提供图片。干杯!
public void begin()
{ List<string> lines = new List<string>();
string line = "";
Console.WriteLine("paste text to begin");
int charCount = 0;
DateTime beg = DateTime.Now;
do
{
Chars = Console.ReadKey(true);
if (Chars.Key == ConsoleKey.Enter)
{
lines.Add(line);
line = "";
}
else
{
line += Chars.KeyChar;
charCount++;
}
} while (charCount < 100000);
Console.WriteLine("100,000 characters ("+lines.Count.ToString("N0")+" lines) in " + DateTime.Now.Subtract(beg).TotalMilliseconds.ToString("N0")+" milliseconds");
}
我正在一台机器上粘贴一个包含长行文本的 5 MB 文件,所有内核都在做其他事情(99% CPU 负载)并在 1.87 秒内获得 1,600 行中的 100,000 个字符。
我看不出你需要保持秩序?如果是这样,请将 Parallel 与分区程序 class 结合使用,因为您正在执行小任务:
例如参见When to use Partitioner class?
这意味着您必须将数据类型更改为 ConcurrentBag
或 ConcurrentDictionary
为什么不用
Parallel.For
要多线程从控制台读取?
如果不是,则尝试使用
将其直接从剪贴板中拉出
https://msdn.microsoft.com/en-us/library/kz40084e(v=vs.110).aspx
在您的场景中,大量时间被浪费在尝试显示插入符号上。您可以禁用在 Windows 中显示的插入符号(我不知道如何在其他平台上执行此操作)。
不幸的是,必要的 API 没有被 .NET 公开(至少在 4.6.1 中)。所以你需要关注 native methods/constants:
internal class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle);
internal const int STD_INPUT_HANDLE = -10;
internal const int ENABLE_ECHO_INPUT = 0x0004;
}
并在从剪贴板接收数据之前按以下方式使用它们:
var handle = NativeMethods.GetStdHandle(NativeMethods.STD_INPUT_HANDLE);
int mode;
NativeMethods.GetConsoleMode(handle, out mode);
mode &= ~NativeMethods.ENABLE_ECHO_INPUT; // disable flag
NativeMethods.SetConsoleMode(handle, mode);
当您完成接收剪贴板数据时,不要忘记恢复控制台模式标志。我希望它能减少你的性能问题。
有关控制台模式的更多信息,请访问 GetConsoleMode
进一步的优化尝试包括:
- 重写无锁的控制台读取代码(因为它在
.NET) 并确保此时没有任何线程与控制台一起工作
片刻。相当昂贵的任务。
- 尝试找到增加标准输入缓冲区大小的方法。但我不确定这是否可能。
- 不要忘记在没有调试的情况下在发布版本中进行测试 %)
我需要从控制台的标准输入流中快速读取数据。输入包含 100.000 行,每行 20 个字符(200 万个字符);用户从剪贴板粘贴它。我的程序运行了大约 3 分钟(非常 很慢;目标是 10 秒)。它看起来像:
var inputData = new string[100000]; // 100.000 rows with 20 chars
for (int i = 0; i < 100000; i++) // Cycle duration is about 3 minutes...
{
inputData[i] = Console.ReadLine();
}
// some processing...
我尝试了什么:
直接:Console.Read,Console.ReadKey-结果一样
Console.In: Read(), ReadLine(), ReadAsync(), ReadLineAsync(), ReadBlock(with各种块大小)、ReadBlockAsync()、ReadToEnd()、ReadToEndAsync() - 相同的结果
new StreamReader(Console.OpenStandardInput(buffer)) 具有不同的缓冲区和块大小 - 相同的结果
开始阅读时隐藏控制台window,阅读结束时显示-加速10%
我尝试从文件中获取输入数据 - 它运行得非常好而且速度很快。但我需要阅读 __ConsoleStream.
我注意到,在进行输入读取时 - 进程 conhost.exe 主动使用处理器。
如何加快输入的读取速度?
更新:
Increasing/decreasing Console.BufferHeight 和 Console.BufferWidth 没有效果
ReadFile
msdn也在慢。但是我注意到一个有趣的事实:ReadFile(handle, buffer, bufferSize, out bytesCount, null); // bufferSize may be very big, but buffer obtains no more than one row (with \r\n). // So, it seems that data passed into InputStream row-by-row syncroniously.
使用本机 WinApi 函数:
WinApi 在 C# 中的使用示例:http://www.pinvoke.net/
你在这里的主要减速是 Console.Read() 和 Console.ReadLine() 都 "echo" 你在屏幕上的文本 - 并且编写文本的过程会减慢你的速度.那么,您要使用的是 Console.Readkey(true),它不会回显粘贴的文本。这是一个在大约 1 秒内写入 100,000 个字符的示例。它可能需要根据您的目的进行一些修改,但我希望这足以为您提供图片。干杯!
public void begin()
{ List<string> lines = new List<string>();
string line = "";
Console.WriteLine("paste text to begin");
int charCount = 0;
DateTime beg = DateTime.Now;
do
{
Chars = Console.ReadKey(true);
if (Chars.Key == ConsoleKey.Enter)
{
lines.Add(line);
line = "";
}
else
{
line += Chars.KeyChar;
charCount++;
}
} while (charCount < 100000);
Console.WriteLine("100,000 characters ("+lines.Count.ToString("N0")+" lines) in " + DateTime.Now.Subtract(beg).TotalMilliseconds.ToString("N0")+" milliseconds");
}
我正在一台机器上粘贴一个包含长行文本的 5 MB 文件,所有内核都在做其他事情(99% CPU 负载)并在 1.87 秒内获得 1,600 行中的 100,000 个字符。
我看不出你需要保持秩序?如果是这样,请将 Parallel 与分区程序 class 结合使用,因为您正在执行小任务:
例如参见When to use Partitioner class?
这意味着您必须将数据类型更改为 ConcurrentBag
或 ConcurrentDictionary
为什么不用
Parallel.For
要多线程从控制台读取? 如果不是,则尝试使用
将其直接从剪贴板中拉出https://msdn.microsoft.com/en-us/library/kz40084e(v=vs.110).aspx
在您的场景中,大量时间被浪费在尝试显示插入符号上。您可以禁用在 Windows 中显示的插入符号(我不知道如何在其他平台上执行此操作)。
不幸的是,必要的 API 没有被 .NET 公开(至少在 4.6.1 中)。所以你需要关注 native methods/constants:
internal class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle);
internal const int STD_INPUT_HANDLE = -10;
internal const int ENABLE_ECHO_INPUT = 0x0004;
}
并在从剪贴板接收数据之前按以下方式使用它们:
var handle = NativeMethods.GetStdHandle(NativeMethods.STD_INPUT_HANDLE);
int mode;
NativeMethods.GetConsoleMode(handle, out mode);
mode &= ~NativeMethods.ENABLE_ECHO_INPUT; // disable flag
NativeMethods.SetConsoleMode(handle, mode);
当您完成接收剪贴板数据时,不要忘记恢复控制台模式标志。我希望它能减少你的性能问题。 有关控制台模式的更多信息,请访问 GetConsoleMode
进一步的优化尝试包括:
- 重写无锁的控制台读取代码(因为它在 .NET) 并确保此时没有任何线程与控制台一起工作 片刻。相当昂贵的任务。
- 尝试找到增加标准输入缓冲区大小的方法。但我不确定这是否可能。
- 不要忘记在没有调试的情况下在发布版本中进行测试 %)