Быстрое считывание ввода в консоль

Мне нужно для быстрого чтения данных из стандартного потока ввода консоли. Вход состоит из 100 000 строк по 20 символов (2 миллиона символов); пользователь вставляет его из буфера обмена. Моя процедура работает около 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(buffer)) с различным размером буфера и блока - тот же результат

  • Скрыть окно консоли в начале чтения и показать его при завершении чтения - ускорение 10%

  • Я попытался получить входные данные из файла - он работает отлично и быстро. Но мне нужно читать из __ConsoleStream.

Я заметил, что во время ввода данных процесс - процесс 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.
    

Ответ 1

Используйте собственную функцию WinApi:

  • Получить дескриптор ввода: GetStdHandle msdn
  • Прочитайте 22 байта (с endline/n/r) с помощью ReadFile (Вместо ReadLine) msdn

Примеры использования WinApi в С#: http://www.pinvoke.net/

Ответ 2

Ваше основное замедление здесь заключается в том, что Console.Read() и Console.ReadLine() как "эхо" вашего текста на экране, так и процесс написания текста замедляет вас. Таким образом, вы хотите использовать Console.Readkey(true), который не повторяет вложенный текст. Вот пример, который записывает 100 000 символов за 1 секунду. Для ваших целей может потребоваться некоторое изменение, но я надеюсь, что это достаточно, чтобы дать вам картину. Ура!

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-мегабайтный файл с длинными строками текста на машине со всеми ядрами, которые делают другие вещи (99% загрузки процессора) и получают 100 000 символов в 1600 строк за 1,87 секунды.

Ответ 3

Я не вижу, что вам нужно сохранить порядок? Если это так, используйте Parallel в сочетании с классом разделителей, поскольку выполняете небольшие задачи:

См. Когда использовать класс Partitioner?, например

Это означает, что вам необходимо изменить тип данных на ConcurrentBag или ConcurrentDictionary

Ответ 4

Почему бы не использовать

Parallel.For

Чтобы многопотоковое чтение с консоли? Если нет, попробуйте вытащить его прямо из буфера обмена, используя

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