快速读取控制台输入
本文关键字:输入 控制台 读取 | 更新日期: 2023-09-27 18:19:25
我需要从控制台的标准输入流快速读取数据。输入由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...
What's I tried:
- 直接
:控制台。阅读,控制台。ReadKey -相同的结果
控制台。在: Read(), ReadLine(), ReadAsync(), ReadLineAsync(), ReadBlock(与各种块大小), ReadBlockAsync(), ReadToEnd(), ReadToEndAsync() -相同的结果
new StreamReader(Console.OpenStandardInput(buffer))与不同的缓冲区和块大小-相同的结果
在读取开始时隐藏控制台窗口,并在读取完成时显示-加速度10%
我试着从文件中获得输入数据-它的工作完美而快速。但是我需要从__ConsoleStream中读取。
我注意到,当输入读取正在进行时- process conhost.exe积极使用处理器。
如何加快输入的读取速度?
乌利希期刊指南:
增加/减少控制台。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.
在您的场景中,由于试图显示插入符号而浪费了大量时间。您可以禁用在Windows中显示的插入符号(我不知道如何在其他平台上这样做)。
不幸的是,. net没有公开必要的API(至少在4.6.1中)。所以你需要以下原生方法/常量:
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),并确保没有任何线程与控制台一起工作的时刻。相当昂贵的任务。试图找到一种方法来增加stdin缓冲区的大小。但我不确定这是否可能。
- 不要忘记在没有调试的情况下在发布版本中进行测试%)
你在这里的主要减速是Console.Read()和Console.ReadLine()都在屏幕上"回显"你的文本-并且写文本的过程减慢了你的速度。那么,您想使用的是Console.Readkey(true),它不会回显粘贴的文本。这里有一个在1秒内写10万个字符的例子。它可能需要一些修改为您的目的,但我希望它足以给你的图片。干杯!
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秒内得到1600行100,000个字符。
使用本地WinApi函数:
- 获取输入句柄:
GetStdHandle
msdn - 用
ReadFile
(代替ReadLine
) msdn读取22字节(带endline/n/r)
WinApi在c#中的应用示例:http://www.pinvoke.net/
我看不出你需要维持秩序。如果是这样,请将Parallel与分区器类结合使用,因为您正在执行小型任务:
参见何时使用分区器类?例如
这意味着您必须将数据类型更改为ConcurrentBag
或ConcurrentDictionary
为什么不使用
Parallel.For
要多线程从控制台读取吗?如果没有,那么尝试使用
直接从剪贴板中拉出它。https://msdn.microsoft.com/en-us/library/kz40084e (v = vs.110) . aspx