快速读取控制台输入

本文关键字:输入 控制台 读取 | 更新日期: 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:

    直接
  1. :控制台。阅读,控制台。ReadKey -相同的结果

  2. 控制台。在: Read(), ReadLine(), ReadAsync(), ReadLineAsync(), ReadBlock(与各种块大小), ReadBlockAsync(), ReadToEnd(), ReadToEndAsync() -相同的结果

  3. new StreamReader(Console.OpenStandardInput(buffer))与不同的缓冲区和块大小-相同的结果

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

  5. 我试着从文件中获得输入数据-它的工作完美而快速。但是我需要从__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函数:

    1. 获取输入句柄:GetStdHandle msdn
    2. ReadFile(代替ReadLine) msdn读取22字节(带endline/n/r)

    WinApi在c#中的应用示例:http://www.pinvoke.net/

    我看不出你需要维持秩序。如果是这样,请将Parallel与分区器类结合使用,因为您正在执行小型任务:

    参见何时使用分区器类?例如

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

    为什么不使用

    Parallel.For
    

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

    直接从剪贴板中拉出它。

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