Re[5]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 24.01.20 13:20
Оценка:
Здравствуйте, 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? Мне тогда надо будет делать поправку на размер буфера?
Кодом людям нужно помогать!
Re[6]: FileStream, buffer, оптимальное чтение
От: romangr Россия  
Дата: 24.01.20 13:39
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Два вопроса:

S>1)правильно ли я понимаю, что первый раз CountBytes будет итерироваться в холостую по пустому буферу?
S>2) а если мне надо посчитать кол-во нулевых байтов в файле, что-то типа 0x00? Мне тогда надо будет делать поправку на размер буфера?

1. Нет, при первом проходе будет bytesRead=0 и холостой итерации не будет.
2. Никаких поправок делать не нужно.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[7]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 24.01.20 13:48
Оценка:
Здравствуйте, romangr, Вы писали:

R>1. Нет, при первом проходе будет bytesRead=0 и холостой итерации не будет.


Упс, Length == 0, неправ.
Кодом людям нужно помогать!
Re[7]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 24.01.20 14:12
Оценка:
Здравствуйте, 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 ?
Кодом людям нужно помогать!
Отредактировано 24.01.2020 14:16 Sharov . Предыдущая версия .
Re[8]: FileStream, buffer, оптимальное чтение
От: romangr Россия  
Дата: 24.01.20 14:15
Оценка: +1
Здравствуйте, Sharov, Вы писали:

S>Этот в 4 раза лучше моего -- Вылавливал косяк с переполнением в Worker, ибо у меня буффер может быть Int32.maxval, соотв. position и step должны быть long.

S>И почему Task.Run, а не Factory.StartNew ?

Безо всякого умысла, что первое в голову пришло, то и поставил. Таме еще косяк есть — нужно счетчик байт long делать, у меня там int, что не есть гуд для больших файлов.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[9]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 24.01.20 14:31
Оценка:
Здравствуйте, 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, это не увидел.
Кодом людям нужно помогать!
Re[10]: FileStream, buffer, оптимальное чтение
От: alexzzzz  
Дата: 27.01.20 12:13
Оценка: 5 (1)
Здравствуйте, Sharov, Вы писали:

S>>>И почему Task.Run, а не Factory.StartNew ?

R>>Безо всякого умысла, что первое в голову пришло, то и поставил.
S>Я сейчас не помню, но советую Factory использовать, а не Run. Run может не стартовать таску, или что-то вроде этого. Лень гуглить.

Task.Run просто создаёт и запускает таск в пуле потоков и всё. У фабрики же имеется куча дополнительных настроек. Если они не нужны, рекомендуют Task.Run.

https://devblogs.microsoft.com/pfxteam/task-run-vs-task-factory-startnew/
Отредактировано 27.01.2020 12:14 alexzzzz . Предыдущая версия .
Re[11]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 27.01.20 12:19
Оценка:
Здравствуйте, alexzzzz, Вы писали:

A>Task.Run просто создаёт и запускает таск в пуле потоков и всё. У фабрики же имеется куча дополнительных настроек. Если они не нужны, рекомендуют Task.Run.

A>https://devblogs.microsoft.com/pfxteam/task-run-vs-task-factory-startnew/

Ага, значит попутал с экземплярным методом Run (или Start?), когда таска может и не стартоваться. Были какие-то свои грабли, не рекомендовали использовать это API.
Кодом людям нужно помогать!
Re[12]: FileStream, buffer, оптимальное чтение
От: alexzzzz  
Дата: 27.01.20 13:56
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Ага, значит попутал с экземплярным методом Run (или Start?), когда таска может и не стартоваться. Были какие-то свои грабли, не рекомендовали использовать это API.


Может быть, имелось в виду, что фабрика и экземплярный Start позволяют явно задать планировщик, под которым таск будет выполняться, а у планировщиков может быть сложная логика по отправке тасков на исполнение и результата можно не дождаться? Какой-нибудь кастомный планировщик может копить задания в очереди до очередного новолуния с четверга на пятницу, а штатный TaskScheduler.Default отправляет таски на исполнение в пул потоков (хотя пул тоже не резиновый).

Или ещё можно соединять таски в иерархии родительских-дочерних. Тогда если кто-то из них выбросит исключение, другие могут не выполниться. В этом я не разбираюсь.
Re[13]: FileStream, buffer, оптимальное чтение
От: Sharov Россия  
Дата: 27.01.20 14:22
Оценка:
Здравствуйте, alexzzzz, Вы писали:

A>Может быть, имелось в виду, что фабрика и экземплярный Start позволяют явно задать планировщик, под которым таск будет выполняться, а у планировщиков может быть сложная логика по отправке тасков на исполнение и результата можно не дождаться? Какой-нибудь кастомный планировщик может копить задания в очереди до очередного новолуния с четверга на пятницу, а штатный TaskScheduler.Default отправляет таски на исполнение в пул потоков (хотя пул тоже не резиновый).


Была проблема, что я ожидал заверешния таска, т.е. был код с ContinueWith, а таска даже не начиналась.

A>Или ещё можно соединять таски в иерархии родительских-дочерних. Тогда если кто-то из них выбросит исключение, другие могут не выполниться. В этом я не разбираюсь.


Там много нюансов.
Кодом людям нужно помогать!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.