Здравствуйте, alexzzzz, Вы писали: A>>Вероятно, где-то присутствуют какие-то задержки. Может быть, нет упреждающего чтения и его можно как-то включить. A>Или 4096-байтные страницы памяти дают слишком большой оверхед и большие двухмегабайтные помогли бы, но не знаю, как ОС об этом сообщить. A>Следующий вариант программы работает стабильно быстрее 800 Мб/с. Два буфера ― пока один заполняется данными, во втором ищутся нужные байты, потом буферы меняются местами. Оптимальный размер буферов почему-то в районе сотен килобайт. Если уменьшать или увеличивать, скорость падает. Функцию поиска ускорять нет смысла. Если её вообще убрать, скорость работы не увеличивается. A>
Скрытый текст
using System;
A>using System.Diagnostics;
A>using System.IO;
A>using System.Threading.Tasks;
[q]
A>internal class Program
A>{
A> private static async Task Main()
A> {
A> var filename = @"C:\Users\alexz\Desktop\H.VHDX";
A> var length = new FileInfo(filename).Length;
A> var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
A> long result = 0;
A> var timer = Stopwatch.StartNew();
A> {
A> const int BUFFER_SIZE = 256 * 1024;
A> var buffers = new[] { new byte[BUFFER_SIZE], new byte[BUFFER_SIZE] };
A> var activeBufferIndex = 0;
A> var bytesRead = 0;
A> do
A> {
A> var task = stream.ReadAsync(buffers[activeBufferIndex], 0, buffers[activeBufferIndex].Length);
A> result += CountBytes(0, buffers[1 - activeBufferIndex], bytesRead);
A> bytesRead = await task;
A> activeBufferIndex = 1 - activeBufferIndex;
A> } while (bytesRead > 0);
A> }
A> timer.Stop();
A> Console.WriteLine(result);
A> Console.WriteLine(length * 1000 / timer.ElapsedMilliseconds / 1_000_000 + " Mb/s");
A> }
A> private static long CountBytes(byte reference, byte[] buffer, int size)
A> {
A> var count = 0;
A> for (var i = 0; i < size; i++)
A> {
A> if (buffer[i] == reference) count++;
A> }
A> return count;
A> }
A>}
[/q] A>Пока не знаю, как заставить читаться данные с моего SSD на максимальной скорости. Он может в два с лишним раза быстрее.
Неплохо, в два раза быстрее моей наивной реалзиации --
Два вопроса:
1)правильно ли я понимаю, что первый раз CountBytes будет итерироваться в холостую по пустому буферу?
2) а если мне надо посчитать кол-во нулевых байтов в файле, что-то типа 0x00? Мне тогда надо будет делать поправку на размер буфера?
Здравствуйте, Sharov, Вы писали:
S>Два вопроса: S>1)правильно ли я понимаю, что первый раз CountBytes будет итерироваться в холостую по пустому буферу? S>2) а если мне надо посчитать кол-во нулевых байтов в файле, что-то типа 0x00? Мне тогда надо будет делать поправку на размер буфера?
1. Нет, при первом проходе будет bytesRead=0 и холостой итерации не будет.
2. Никаких поправок делать не нужно.
Здравствуйте, romangr, Вы писали: R>Здравствуйте, romangr, Вы писали: R>>выдает на моем 1TB Samsung 970 EVO+ скорость в 2700 Mb/s R>>что, конечно, меньше чем 3500 в тестах, но для такого тупого алгоритма нормально.
Скрытый текст
R>
Скрытый текст
R>
R>class Program
R>{
R> static async Task Main(string[] args)
R> {
R> const int FILE_FLAG_NO_BUFFERING = 0x20000000;
R> const int BufferSize = 256 * 1024;
R> const string Path = @"c:\iso\myiso.iso";
R> const int TaskNumber = 16;
R> const byte Pattern = 42;
R> var totalLength = new FileInfo(Path).Length;
R> var timer = Stopwatch.StartNew();
R> var tasks = new Task<int>[TaskNumber];
R> for (int i = 0; i < TaskNumber; i++)
R> {
R> var options = (FileOptions) FILE_FLAG_NO_BUFFERING | FileOptions.SequentialScan;
R> var fs = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, options);
R> var position = i * BufferSize;
R> tasks[i] = Task.Run(() => Worker(fs, position, TaskNumber * BufferSize, BufferSize, Pattern));
R> }
R> var result = await Task.WhenAll(tasks);
R> var speed = totalLength * 1000 / timer.ElapsedMilliseconds / 1_000_000;
R> Console.WriteLine($"Read {totalLength} bytes for {timer.ElapsedMilliseconds / 1000.0} seconds. Speed is {speed} Mb/s");
R> Console.WriteLine($"Found {result.Sum()} occurences of byte {Pattern}");
R> }
R> private static int Worker(FileStream fs, long position, int step, int size, byte pattern)
R> {
R> var totalLength = fs.Length;
R> var result = 0;
R> var buffer = new byte[Math.Min(1024, size)];
R> while (position < totalLength)
R> {
R> fs.Seek(position, SeekOrigin.Begin);
R> var count = (int) Math.Min(size, totalLength - position);
R> while (count > 0)
R> {
R> var read = fs.Read(buffer, 0, buffer.Length);
R> for (int i = 0; i < read; i++)
R> {
R> if (buffer[i] == pattern)
R> {
R> result++;
R> }
R> }
R> count -= read;
R> }
R> position += step;
R> }
R> return result;
R> }
R>}
R>
Этот в 4 раза лучше моего -- Вылавливал косяк с переполнением в Worker, ибо у меня буффер может быть Int32.maxval, соотв. position и step должны быть long.
И почему Task.Run, а не Factory.StartNew ?
Здравствуйте, Sharov, Вы писали:
S>Этот в 4 раза лучше моего -- Вылавливал косяк с переполнением в Worker, ибо у меня буффер может быть Int32.maxval, соотв. position и step должны быть long. S>И почему Task.Run, а не Factory.StartNew ?
Безо всякого умысла, что первое в голову пришло, то и поставил. Таме еще косяк есть — нужно счетчик байт long делать, у меня там int, что не есть гуд для больших файлов.
Здравствуйте, romangr, Вы писали:
R>Здравствуйте, Sharov, Вы писали:
S>>Этот в 4 раза лучше моего -- Вылавливал косяк с переполнением в Worker, ибо у меня буффер может быть Int32.maxval, соотв. position и step должны быть long. S>>И почему Task.Run, а не Factory.StartNew ?
R>Безо всякого умысла, что первое в голову пришло, то и поставил.
Я сейчас не помню, но советую Factory использовать, а не Run. Run может не стартовать таску, или что-то вроде этого. Лень гуглить.
R>Таме еще косяк есть — нужно счетчик байт long делать, у меня там int, что не есть гуд для больших файлов.
Я завозился с переполнение position и step, это не увидел.
Здравствуйте, Sharov, Вы писали:
S>>>И почему Task.Run, а не Factory.StartNew ? R>>Безо всякого умысла, что первое в голову пришло, то и поставил. S>Я сейчас не помню, но советую Factory использовать, а не Run. Run может не стартовать таску, или что-то вроде этого. Лень гуглить.
Task.Run просто создаёт и запускает таск в пуле потоков и всё. У фабрики же имеется куча дополнительных настроек. Если они не нужны, рекомендуют Task.Run.
Ага, значит попутал с экземплярным методом Run (или Start?), когда таска может и не стартоваться. Были какие-то свои грабли, не рекомендовали использовать это API.
Здравствуйте, Sharov, Вы писали:
S>Ага, значит попутал с экземплярным методом Run (или Start?), когда таска может и не стартоваться. Были какие-то свои грабли, не рекомендовали использовать это API.
Может быть, имелось в виду, что фабрика и экземплярный Start позволяют явно задать планировщик, под которым таск будет выполняться, а у планировщиков может быть сложная логика по отправке тасков на исполнение и результата можно не дождаться? Какой-нибудь кастомный планировщик может копить задания в очереди до очередного новолуния с четверга на пятницу, а штатный TaskScheduler.Default отправляет таски на исполнение в пул потоков (хотя пул тоже не резиновый).
Или ещё можно соединять таски в иерархии родительских-дочерних. Тогда если кто-то из них выбросит исключение, другие могут не выполниться. В этом я не разбираюсь.
Здравствуйте, alexzzzz, Вы писали:
A>Может быть, имелось в виду, что фабрика и экземплярный Start позволяют явно задать планировщик, под которым таск будет выполняться, а у планировщиков может быть сложная логика по отправке тасков на исполнение и результата можно не дождаться? Какой-нибудь кастомный планировщик может копить задания в очереди до очередного новолуния с четверга на пятницу, а штатный TaskScheduler.Default отправляет таски на исполнение в пул потоков (хотя пул тоже не резиновый).
Была проблема, что я ожидал заверешния таска, т.е. был код с ContinueWith, а таска даже не начиналась.
A>Или ещё можно соединять таски в иерархии родительских-дочерних. Тогда если кто-то из них выбросит исключение, другие могут не выполниться. В этом я не разбираюсь.