1) что будет, если я в качестве буффера в конструкторе попрошу аллоцировать недоступное системе кол-во памяти, т.е. скажем 2гб (int bufSize), а системе доступно только 1гб и файла подкачки нет?
Аллоцирует на свое усмотрение или выкинет что-то типа OOM исключения?
2) Как лучше считывать огромный файл( допустим надо посчитать кол-во вхождений определенного байта), памяти предостаточно.
Ничего лучше чем аллоцировать 2гб буффера и читать его через ReadByte я не придумал. Может бить его на чанки, и кажды поток будет обрабатывать свой чанк?
Но тогда я буду дважы проходить по буферу -- 1 раз при копировании для соотв таски, второй для подсчета байта, плюс доп. аллокации памяти.
.....
using(FileStream
fileStream = new FileStream(fileName, ..., maxBuf,...))
{
var counter = 0;
var b = fileStream.ReadByte();
while (b != -1)
{
if (b == neededByte) counter++;
b = fileStream.ReadByte();
}
}
....
Здравствуйте, Sharov, Вы писали:
S>2) Как лучше считывать огромный файл( допустим надо посчитать кол-во вхождений определенного байта), памяти предостаточно.
Здравствуйте, Sharov, Вы писали:
S>Ничего лучше чем аллоцировать 2гб буффера и читать его через ReadByte я не придумал. Может бить его на чанки, и кажды поток будет обрабатывать свой чанк?
Тут ты упираешься в скорость чтения с диска, а не подсчёт байтиков. Будет ли чтение быстрее в многопотоке — большой вопрос, скорее нет.
Здравствуйте, ltc, Вы писали:
ltc>Тут ты упираешься в скорость чтения с диска, а не подсчёт байтиков. Будет ли чтение быстрее в многопотоке — большой вопрос, скорее нет.
Здравствуйте, ltc, Вы писали:
ltc>Здравствуйте, Sharov, Вы писали:
S>>2) Как лучше считывать огромный файл( допустим надо посчитать кол-во вхождений определенного байта), памяти предостаточно.
ltc>Memory mapped file.
Вариант, но тогда уж MemoryStream прокатил бы. Предполагалось, что весь файл в память грузить не желательно.
Здравствуйте, Sharov, Вы писали:
S>>>2) Как лучше считывать огромный файл( допустим надо посчитать кол-во вхождений определенного байта), памяти предостаточно.
ltc>>Memory mapped file.
S>Вариант, но тогда уж MemoryStream прокатил бы. Предполагалось, что весь файл в память грузить не желательно.
Ты почитай про memory mapped file, там все уже сделано.
Здравствуйте, Sharov, Вы писали:
S>Вот тут можно подробнее, что значит читать в несколько потоков, если у меня fs (currentpos в потоке ) не разделяемый?
Здравствуйте, romangr, Вы писали:
R>Здравствуйте, Sharov, Вы писали:
S>>Вот тут можно подробнее, что значит читать в несколько потоков, если у меня fs (currentpos в потоке ) не разделяемый?
R>MemoryMappedFile.CreateViewStream
Ясно, т.е. через filestream быстрее не сделать? А mmf уже отдельный разговор.
Здравствуйте, Sharov, Вы писали:
S>Ясно, т.е. через filestream быстрее не сделать? А mmf уже отдельный разговор.
Я думаю, что используя один FileStream быстрее не получится.
Но может и не нужно? Подобрать размер буфера, при котором скорость достаточна и все.
Зато алгоритм проще и меньше шансов накосячить.
Здравствуйте, Sharov, Вы писали:
S>Ясно, т.е. через filestream быстрее не сделать? А mmf уже отдельный разговор.
Ну конкретно в твоем примере можно как минимум заменить ReadByte на ReadAsync(Byte[], Int32, Int32, CancellationToken) с каким-то достаточно большим буфером и разнести чтение из файла и подсчет байтов на разные потоки.
Здравствуйте, romangr, Вы писали: ltc>>Тут ты упираешься в скорость чтения с диска, а не подсчёт байтиков. Будет ли чтение быстрее в многопотоке — большой вопрос, скорее нет. R>Для SSD чтение в несколько потоков имеет смысл.
Что в один поток, что в несколько, у меня не получилось разогнаться больше 500 Мб/с.
Скрытый текст
using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
internal unsafe class Program
{
private static void Main()
{
var filename = @"C:\Users\alexz\Desktop\H.VHDX"; // ~100 Гбvar mmf = MemoryMappedFile.CreateFromFile(filename, FileMode.Open);
var accessor = mmf.CreateViewAccessor();
var length = accessor.Capacity;
var handle = accessor.SafeMemoryMappedViewHandle;
byte* start = default;
handle.AcquirePointer(ref start);
var timer = Stopwatch.StartNew();
var result = CountBytes(0, start, start + length);
timer.Stop();
Console.WriteLine(result);
Console.WriteLine(length * 1000 / timer.ElapsedMilliseconds / 1_000_000 + " Mb/s");
}
private static long CountBytes(byte reference, byte* startPointer, byte* endPointer)
{
long count = 0;
byte* pointer = startPointer;
while (pointer < endPointer)
{
if (*pointer++ == reference) count++;
}
return count;
}
}
Если файл уже в кэше, то эта простая программа считает количество нулевых байт со скоростью ~1,1 Гб/с. А если данные приходится читать с накопителя, то загрузка процессора падает и средняя скорость оказывается в районе 400-500 Мб/с, хотя мой NVMe может отдавать 2 Гб/с.
Вероятно, где-то присутствуют какие-то задержки. Может быть, нет упреждающего чтения и его можно как-то включить.
A>Вероятно, где-то присутствуют какие-то задержки. Может быть, нет упреждающего чтения и его можно как-то включить.
Или 4096-байтные страницы памяти дают слишком большой оверхед и большие двухмегабайтные помогли бы, но не знаю, как ОС об этом сообщить.
Следующий вариант программы работает стабильно быстрее 800 Мб/с. Два буфера ― пока один заполняется данными, во втором ищутся нужные байты, потом буферы меняются местами. Оптимальный размер буферов почему-то в районе сотен килобайт. Если уменьшать или увеличивать, скорость падает. Функцию поиска ускорять нет смысла. Если её вообще убрать, скорость работы не увеличивается.
Скрытый текст
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
internal class Program
{
private static async Task Main()
{
var filename = @"C:\Users\alexz\Desktop\H.VHDX";
var length = new FileInfo(filename).Length;
var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
long result = 0;
var timer = Stopwatch.StartNew();
{
const int BUFFER_SIZE = 256 * 1024;
var buffers = new[] { new byte[BUFFER_SIZE], new byte[BUFFER_SIZE] };
var activeBufferIndex = 0;
var bytesRead = 0;
do
{
var task = stream.ReadAsync(buffers[activeBufferIndex], 0, buffers[activeBufferIndex].Length);
result += CountBytes(0, buffers[1 - activeBufferIndex], bytesRead);
bytesRead = await task;
activeBufferIndex = 1 - activeBufferIndex;
} while (bytesRead > 0);
}
timer.Stop();
Console.WriteLine(result);
Console.WriteLine(length * 1000 / timer.ElapsedMilliseconds / 1_000_000 + " Mb/s");
}
private static long CountBytes(byte reference, byte[] buffer, int size)
{
var count = 0;
for (var i = 0; i < size; i++)
{
if (buffer[i] == reference) count++;
}
return count;
}
}
Пока не знаю, как заставить читаться данные с моего SSD на максимальной скорости. Он может в два с лишним раза быстрее.
Здравствуйте, alexzzzz, Вы писали: A>Пока не знаю, как заставить читаться данные с моего SSD на максимальной скорости. Он может в два с лишним раза быстрее.
Такой код
Скрытый текст
class Program
{
static async Task Main(string[] args)
{
const int BufferSize = 128 * 1024;
const string Path = @"c:\iso\myiso.iso";
const int TaskNumber = 16;
const byte Pattern = 42;
var totalLength = new FileInfo(Path).Length;
var timer = Stopwatch.StartNew();
var tasks = new Task<int>[TaskNumber];
for (int i = 0; i < TaskNumber; i++)
{
var fs = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.SequentialScan);
var position = totalLength / TaskNumber * i;
fs.Seek(position, SeekOrigin.Begin);
var length = i == TaskNumber - 1 ? totalLength - position : totalLength / TaskNumber;
tasks[i] = Task.Run(() => Worker(fs, length, Pattern));
}
var result = await Task.WhenAll(tasks);
var speed = totalLength * 1000 / timer.ElapsedMilliseconds / 1_000_000;
Console.WriteLine($"Read {totalLength} bytes for {timer.ElapsedMilliseconds / 1000} seconds. Speed is {speed} Mb/s");
Console.WriteLine($"Found {result.Sum()} occurences of byte {Pattern}");
}
static int Worker(FileStream fs, long length, byte pattern)
{
var count = 0;
while (length > 0)
{
if (fs.ReadByte() == pattern)
{
count++;
}
--length;
}
return count;
}
}
выдает на моем 1TB Samsung 970 EVO+ скорость в 2700 Mb/s
что, конечно, меньше чем 3500 в тестах, но для такого тупого алгоритма нормально.
Здравствуйте, romangr, Вы писали: R>выдает на моем 1TB Samsung 970 EVO+ скорость в 2700 Mb/s R>что, конечно, меньше чем 3500 в тестах, но для такого тупого алгоритма нормально.
Скрытый текст
class Program
{
static async Task Main(string[] args)
{
const int FILE_FLAG_NO_BUFFERING = 0x20000000;
const int BufferSize = 256 * 1024;
const string Path = @"c:\iso\myiso.iso";
const int TaskNumber = 16;
const byte Pattern = 42;
var totalLength = new FileInfo(Path).Length;
var timer = Stopwatch.StartNew();
var tasks = new Task<int>[TaskNumber];
for (int i = 0; i < TaskNumber; i++)
{
var options = (FileOptions) FILE_FLAG_NO_BUFFERING | FileOptions.SequentialScan;
var fs = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, options);
var position = i * BufferSize;
tasks[i] = Task.Run(() => Worker(fs, position, TaskNumber * BufferSize, BufferSize, Pattern));
}
var result = await Task.WhenAll(tasks);
var speed = totalLength * 1000 / timer.ElapsedMilliseconds / 1_000_000;
Console.WriteLine($"Read {totalLength} bytes for {timer.ElapsedMilliseconds / 1000.0} seconds. Speed is {speed} Mb/s");
Console.WriteLine($"Found {result.Sum()} occurences of byte {Pattern}");
}
private static int Worker(FileStream fs, long position, int step, int size, byte pattern)
{
var totalLength = fs.Length;
var result = 0;
var buffer = new byte[Math.Min(1024, size)];
while (position < totalLength)
{
fs.Seek(position, SeekOrigin.Begin);
var count = (int) Math.Min(size, totalLength - position);
while (count > 0)
{
var read = fs.Read(buffer, 0, buffer.Length);
for (int i = 0; i < read; i++)
{
if (buffer[i] == pattern)
{
result++;
}
}
count -= read;
}
position += step;
}
return result;
}
}
После небольшого допиливания алгоритма теперь выдает 3450 Mb/s.
R> tasks[i] = Task.Run(() => Worker(fs, position, TaskNumber * BufferSize, BufferSize, 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);
Что-то я не понял этот момент. Они же тут в параллель двигают позицию в потоке туда-сюда. Как они не мешают друг другу?
В смысле работать-то оно будет, но точно ли оно считывает то, что нужно и почему так?
Здравствуйте, fmiracle, Вы писали:
F>Что-то я не понял этот момент. Они же тут в параллель двигают позицию в потоке туда-сюда. Как они не мешают друг другу? F>В смысле работать-то оно будет, но точно ли оно считывает то, что нужно и почему так?
Делаем X задач, для каждой устанавливаем начальную позицию
0, BufferSize, 2 * BufferSize, 3 * BufferSize и т.д.
каждая читает из стрима BufferSize байт.
когда каждая прочитает свой кусок, сдвигаем позицию для каждой на BufferSize * X.
В результате у каждой свой кусок, и они не пересекаются между собой.
Можно Worker упростить чтобы проще понять было, но работать будет медленнее.
private static int Worker2(FileStream fs, long position, int step, int size, byte pattern)
{
var totalLength = fs.Length;
var result = 0;
while (position < totalLength)
{
fs.Seek(position, SeekOrigin.Begin);
var count = (int) Math.Min(size, totalLength - position);
while (count > 0)
{
if (fs.ReadByte() == pattern)
{
result++;
}
--count;
}
position += step;
}
return result;
}
Здравствуйте, blp, Вы писали:
A>>Пока не знаю, как заставить читаться данные с моего SSD на максимальной скорости. blp>Пробовали FileOptions.SequentalScan (до кучи — FileOptions.Asynchronous)?
Не помогло. Оказалось, что если в CrystalDiskMark в параметрах теста выставить Queues=1, Threads=1, то накопитель выдаёт не больше гигабайта в секунду. Если либо queues, либо threads увеличивать, то скорость растёт и где-то в районе 16 достигает максимума.