快速读取控制台输入

时间:2015-10-26 09:28:41

标签: c# .net input stream console

我需要来自控制台的标准输入流快速阅读数据。输入包含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...

我尝试了什么:

  1. 直接:Console.Read,Console.ReadKey - 结果相同

  2. Console.In :Read(),ReadLine(),ReadAsync(),ReadLineAsync(),ReadBlock(各种块大小),ReadBlockAsync( ),ReadToEnd(),ReadToEndAsync() - 结果相同

  3. 新的StreamReader(Console.OpenStandardInput(缓冲区)),具有各种缓冲区和块大小 - 结果相同

  4. 在开始阅读时隐藏控制台窗口,并在阅读完成后显示 - 加速10%

  5. 我尝试从文件中获取输入数据 - 它的工作完美且快速。但我需要从__ConsoleStream中读取。

  6. 我注意到,在输入正在进行中时 - 进程 conhost.exe 会主动使用处理器。

    如何加快阅读输入?

    UPD:

    1. 增加/减少Console.BufferHeight和Console.BufferWidth无效

    2. 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.
      

5 个答案:

答案 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

进一步的优化尝试可以包括:

  • 重写控制台读取的无锁代码(如在 .NET),并确保当时没有任何线程可用于控制台 时刻。任务很昂贵。
  • 尝试找到增加stdin缓冲区大小的方法。但我不确定是否有可能。
  • 别忘了在不调试%的情况下在发行版中进行测试

答案 1 :(得分:0)

使用原生WinApi功能:

  1. 获取输入句柄:GetStdHandle msdn
  2. 使用ReadFile(而不是ReadLinemsdn
  3. 读取22个字节(带有endline / n / r)

    在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?

这意味着您必须将数据类型更改为ConcurrentBagConcurrentDictionary

答案 4 :(得分:-2)

为什么不使用

Parallel.For

要从控制台多线程读取? 如果没有,请尝试使用

直接从剪贴板中取出

https://msdn.microsoft.com/en-us/library/kz40084e(v=vs.110).aspx