Ответ 1
Используйте собственную функцию WinApi:
- Получить дескриптор ввода:
GetStdHandle
msdn - Прочитайте 22 байта (с endline/n/r) с помощью
ReadFile
(ВместоReadLine
) msdn
Примеры использования WinApi в С#: http://www.pinvoke.net/
Мне нужно для быстрого чтения данных из стандартного потока ввода консоли. Вход состоит из 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:
Увеличение/уменьшение 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.
Используйте собственную функцию WinApi:
GetStdHandle
msdnReadFile
(Вместо ReadLine
) msdnПримеры использования WinApi в С#: http://www.pinvoke.net/
Ваше основное замедление здесь заключается в том, что 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 секунды.
Я не вижу, что вам нужно сохранить порядок? Если это так, используйте Parallel в сочетании с классом разделителей, поскольку выполняете небольшие задачи:
См. Когда использовать класс Partitioner?, например
Это означает, что вам необходимо изменить тип данных на ConcurrentBag
или ConcurrentDictionary
Почему бы не использовать
Parallel.For
Чтобы многопотоковое чтение с консоли? Если нет, попробуйте вытащить его прямо из буфера обмена, используя
https://msdn.microsoft.com/en-us/library/kz40084e(v=vs.110).aspx