我需要来自控制台的标准输入流的快速阅读数据。输入包含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(各种块大小),ReadBlockAsync( ),ReadToEnd(),ReadToEndAsync() - 结果相同
新的StreamReader(Console.OpenStandardInput(缓冲区)),具有各种缓冲区和块大小 - 结果相同
在开始阅读时隐藏控制台窗口,并在阅读完成后显示 - 加速10%
我尝试从文件中获取输入数据 - 它的工作完美且快速。但我需要从__ConsoleStream中读取。
我注意到,在输入正在进行中时 - 进程 conhost.exe 会主动使用处理器。
如何加快阅读输入?
UPD:
增加/减少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.
答案 0 :(得分:1)
在您的情况下,尝试显示插入符号会浪费大量时间。您可以禁用在Windows中显示的插入符号(我不知道如何在其他平台上执行此操作)。
不幸的是,.NET没有公开必要的API(至少在4.6.1中)。因此,您需要遵循 native 方法/常量:
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
进一步的优化尝试可以包括:
答案 1 :(得分:0)
使用原生WinApi功能:
GetStdHandle
msdn ReadFile
(而不是ReadLine
)msdn 在C#中使用WinApi的示例:http://www.pinvoke.net/
答案 2 :(得分:0)
这里的主要减速因素是Console.Read()和Console.ReadLine()都在屏幕上“回显”你的文字 - 而写文字的过程会减慢你的速度。那么你想要使用的是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个字符。
答案 3 :(得分:0)
我没有看到您需要保留订单?如果是这样,请将Parallel与partitioner类结合使用,因为您正在执行小任务:
例如,请参阅When to use Partitioner class?
这意味着您必须将数据类型更改为ConcurrentBag
或ConcurrentDictionary
答案 4 :(得分:-2)
为什么不使用
Parallel.For
要从控制台多线程读取? 如果没有,请尝试使用
直接从剪贴板中取出https://msdn.microsoft.com/en-us/library/kz40084e(v=vs.110).aspx