[Epic facepalm]
Достал исходник старого-старого теста Чеперси, портировал на нынешнююю версию.
Итог (структура и объём данных идентичные):
2006 год. Хилая для того времени машина, искусственно замедленная до 1 гигагерца.
Время загрузки - около 2.2 микросекунды на объект.
2014 год. Современный ультрабук пальцы веером.
Время загрузки - около 7 микросекунд на объект.
Что же я нахимичил то?
Ведь сплошные улучшения.
Вот что значит несколько лет ковыряться с тестовым набором данных из пары дюжин объектов
Пора опять за буттлнеками гоняться с выбивалкой для ковров
Добавлено спустя 13 часов 13 минут 12 секунд:Я
убью TFileStream!
Подстерегу в тёмной подворотне, и замордую ржавым, тупым тапочком!
Естественно, про буферизацию оно даже не слышало. При записи одного байтика сразу бежит вызывать Win32 API, конкретно -
WriteFileКороче. Подменил прямое обращение к TFileStream на TMemoryStream, который потом уже сливается в файловый поток одним вызовом метода SaveToStream.
Даже такая неэффективная двойная сопля
ускорила сериализацию на порядок!
Теперь сохранение занимает 800 наносекунд на объект вместо 9000. Из них примерно две трети - запись структуры данных в TMemoryStream, и одна треть - слив в файл, на 280 Мб/с, что уже похоже на производительность SSD диска.
Вывод: буду восстанавливать делать внутреннюю буферизацию, которую в своё время похерил, понадеявшись на TStream. Авось, ещё немного выжму.
Добавлено спустя 1 час 55 минут 34 секунды:F*** YEAH! Чтение - 500 наносекунд на объект, запись - 320 наносекунд.
И это при том, что структура данных намеренно неэффективная (цепочка из десятков тысяч объектов, соединённых полями следующий/предыдущий). Стек насилуется страшно, рекурсия такая, что 50Мб еле хватает.
Мдя... Тридцатикратное ускорение благодаря всего лишь тупой буферизации, слабанной за час на коленке.
Вы таки не поверите, насколько MOVE(,,4) оказалась быстрее, чем Stream.WriteDword()...
- Код: Выделить всё
procedure ReadFromBuffer(var v; size: longint); inline;
var a: longint;
begin
if BufferHigh < 0 then begin
if size >= CpsBufferSize then begin
{$ifdef bench_streaming}{$include un_bottleneck_begin}{$endif}
CpsStream.Read(v, size);
{$ifdef bench_streaming}{$include un_bottleneck_end}{$endif}
Exit;
end;
FillBuffer;
end;
if BufferPos + size > BufferHigh + 1 then begin
MOVE(CpsBuffer[BufferPos], pointer(@v)^, BufferHigh + 1 - BufferPos);
if (size - (BufferHigh + 1 - BufferPos)) > CpsBufferSize then begin
{$ifdef bench_streaming}{$include un_bottleneck_begin}{$endif}
CpsStream.Read((pointer(@v) + BufferHigh + 1 - BufferPos)^, size - (BufferHigh + 1 - BufferPos));
{$ifdef bench_streaming}{$include un_bottleneck_end}{$endif}
BufferPos:= 0;
BufferHigh:= -1;
Exit;
end;
a:= BufferHigh + 1 - BufferPos;
FillBuffer;
MOVE(CpsBuffer[0], (pointer(@v) + a)^, size - a);
BufferPos:= size - a;
end
else begin
MOVE(CpsBuffer[BufferPos], v, size);
BufferPos+= size;
end;
end;