[c++] ускорение чтения блочной информации
а как-нить его замэпить в память?
и ежу ж понятно, что надо считывать крупные блоки из файла в оперативку, а потом их перелопачивать в оперативке любым подходящим способом. Т.е. не по 4 байта считывать, а хотя бы килобайт по 10 (а то и по несколько мегабайт) - это как минимум экономит огромное количество вызовов функций (команда call и кроме того такой подход не полагается на качество кеширования (потому что ты сам кешируешь). Конечно этот подход не везде уместен, но в данном случае вроде очень даже.
в данном случае, пожалуй, стрим вполне нормально кеширует.
прыгалок других нет?
А ведь тут была огромная тема по этому поводу... где-то в начале 2005...
то есть кроме считывания всего файла идей нету?это где же ты заметил предложение считывать весь файл?
Каков типичный размер блока ? Без это разговор вообще ни о чем.
в данном случае, пожалуй, стрим вполне нормально кеширует.даже если stream (или ОС) достаточно хорошо кеширует, все равно прямой доступ к буферу многократно повысит скорость работы за счет гораздо меньшего количества call-ов (а call выполняется очень долго, в отличие от большинства других команд x86 процессора).
прыгалок других нет?
очень часто по хедеру ясно, что тело пакета по сути не нужното никуда не денешься, придется считывать все.
Если средний блок - 100 байт, иво-первых: во избежание недоразумений "придется считывать все" это уж конечно не весь файл за раз.
очень часто по хедеру ясно, что тело пакета по сути не нужното никуда не денешься, придется считывать все.
во-вторых: считать промежуточные ненужные блоки в составе считываемого за один вызов большого блока вообще не затратно - они и так уже считаны в кеш, а скопировать блок памяти из кеша в наш буфер один раз обычно намного выгоднее чем вызвать много раз функции (даже если эти функции почти ничего не делают). Поэтому считать все в буфер, а потом его интерпретировать почти без вызовов функций - намного выгоднее чем вызывать много раз функцию считывания мелких блоков (хедеров и нужных блоков) и функцию пропуска ненужных блоков.
НО если с точки зрения стиля программа, пользующаяся кучей вызовов, выглядит красиво, логично и понятно, то не надо портить ее оптимизациями, а надо оставить как есть ИМХО.
Поэтому лучше, конечно, читать большими кусками или вообще замапить на память.
а call выполняется очень долго, в отличие от большинства других команд x86 процессораПросто нет слов Наверное ты 10 (15?) лет назад прочитал КНИГУ, в которой около каждой инструкции было написано время выполнения в тактах, да? Так вот сообщаю тебе - эта КНИГА потеряла актуальность...
---
...Я работаю антинаучным аферистом...
Буферизация в fstream-е разумеется есть, зачем еще свою городить? Хотя возможно я избалован достойными реализациями STL в VS, может в gcc все и по другому?..
---
...Я работаю антинаучным аферистом...
идею понял
то есть достаточно сделать надстройку над istream с таким же интерефейсом (методы .seekg .read но фактически при вызове .read(dst,iLength) он сделает
memcpy(dst,Buffer+iCurrentPointer,iLength);
iCurrentPointer+=iLength;
а буфер - это char [30Mbite].
когда iCurrentPointer подлезет к концу внутреннего буфера-сделать подкачку объемом 30Мбайт.
тем самым мы избежим много мелких чтений из файла
правильно я понял?
---
...Я работаю антинаучным аферистом...
А ещё учти, что пропускная способность сети (у тебя ведь NFS) и жёсткого диска сильно ограничены по сравнению с оперативной памятью, и поэтому вся та оптимизация, о которой ты говоришь - фактически бессмысленна. Системные вызовы оптимизировать - нужно, а остальное - врят ли.
Просто нет слов Наверное ты 10 (15?) лет назад прочитал КНИГУ, в которой около каждой инструкции было написано время выполнения в тактах, да? Так вот сообщаю тебе - эта КНИГА потеряла актуальность...нифига не угадал
кроме книг и интернета у меня еще есть мозг - очень полезный девайс говорят
и вот если им воспользоваться, то и ежу понятно даже без специальных знаний, что jmp/jX, тем более call (и еще более int) должны выполнятся значительно дольше большинства команд на современных процах уже только потому, что получается гораздо менее эффективное кеширование кода и ковейерная оптимизация (хотя в проце конечно могут при этом присутствовать всякие навороты, предсказывающие переходы, и т.п.). Не говоря уже о том, что для call-а (и ret-а) требется доступ к стеку (т.е. к оперативке а доступ к оперативке сам по себе является узким местом.
PS как раз 15 лет назад это было не особо актуально вследствие отсутствия или убогости кеширования и конвейеров.
> (и еще более int) должны выполнятся значительно дольше большинства команд на современных процах
Наглая ложь.
> Не говоря уже о том, что для call-а (и ret-а) требется доступ к стеку
Ещё более наглая ложь.
---
...Я работаю антинаучным аферистом...
> Не говоря уже о том, что для call-а (и ret-а) требется доступ к стекуО, я уже узнаю КОНТРУ, не смотря на ник...
Ещё более наглая ложь.
Поясни.
В менее продвинутых процессорах указатель возврата после прерывания запоминается в регистр.
---
"Narrowness of experience leads to narrowness of imagination."
Ну просто нет смысла фантазировать насчет времени работы какого то кода на основании информации десятилетней давности.просто нет смысла фантазировать насчет использования кем-то литературы/представлений десятилетней давности.
Да скорее всего и в современном процессоре call в 20 раз медленнее чем mov на регистрах.вероятно много больше 20, потому что "Доступ к памяти, которая не в кеше - до сотни тактов." (це, епт)
Надеюсь не надо объяснять где аж дважды последовательно происходит доступ к памяти во время выполнения каждой из команд call и ret? (причем один из этих двух доступов вероятно не в кеше)
Ну и что, любая программа в основном время теряет совсем не там.как раз именно там:
Любой syscall, даже тривиальный - это тысячи, даже десятки тысяч тактов. Доступ к памяти, которая не в кеше - до сотни тактов.Я говорю именно об этом же. Именно поэтому я и предлагаю минимизировать количество вызовов потенциальных syscall-ов и почти гарантированных call+ret, подразумевающих 2 последовательных доступа к опеативке в одной команде, и уж точно гарантированного блока лишних команд (в которых есть условные переходы, надеюсь не требуется уточнять - где именно).
Я уж молчу о том, что вызов функции в С имеет довольно косвенное отношение к инструкции call.во-первых мы говорим о C++, хотя и его это утверждение касается.
во-вторых хоть и косвенное, но очень и очень даже имеет.
Буферизация в fstream-е разумеется есть, зачем еще свою городить?я что не по-русски написал зачем?
Чтобы иметь непосредственный доступ в буфер и избавиться тем самым от многих вызовов функций, которые даже если инлайнятся, все равно жутко тормозят (хотя бы за счет условных переходов внутри них).
Хотя возможно я избалован достойными реализациями STL в VS, может в gcc все и по другому?..fstream не относится к STL (но относится к стандартной библиотеке конечно)
Никак нельзя было обойтись без очередного пустого наезда на gcc в дискуссии не относящейся к этой теме вообще?
Еще скажи, что в VS все stream функции инлайнятся.
Но даже если так, в чем я очень сомневаюсь, все равно это весьма существенная оптимизация.
Там, где заведомо нужен только последовательный доступ, мапинги не рулят.
Тем более, если доступ к файлу идёт по сети, лучше стараться максимально точно и явно контролировать, что и когда читается, чтоб минимизировать количество обращений.
Заканчивать надо на этом месте.
Нет смысла рассуждать в отрыве от компилятора и операционной системы.
---
"Истина всегда конкретна."
В наиболее продвинутых процессорах существует команда "branch and link to register."Ты что головой ударился?
В менее продвинутых процессорах указатель возврата после прерывания запоминается в регистр.
Или я название команд написал с ошибками?
Думаю все кроме тебя догадались, что речь идет о командах call/ret процессоров x86 - о них и только о них я и писал, когда употреблял слова "call" и "ret".
Написанные тобой команды конечно существуют на более продвинутых процесорах чем линейка x86, но и называются они почти всегда по другому (а не call и ret).
Ну так полезай в исходники библиотеки, и посмотри, почему так получается. Интересно же.
И ещё я бы первым делом попробовал написать то же без использования STL / stdio.
Заканчивать надо на этом месте.То что я описывал повысит производительность независимо от компилятора и операционной системы, но степень этого повышения зависит от системы команд и архитектуры процессора и качества реализации stream и оптимизации компилятором. Я подразумевал использование относительно новых x86 процессоров, которыми почти все и пользуются, но и других процессоров это тоже касается - быть может в меньшей степени.
Нет смысла рассуждать в отрыве от компилятора и операционной системы.
---
...Я работаю антинаучным аферистом...
А почему ты не подозревал о наличии разных систем на тех же процессорах?Каких "разных систем"? Операционных систем или систем команд?
И то и другое я подразумевал, о чем прямо сказал в предыдущем посте.
Один чёрт, от библиотеки и операционной системы сильно зависит.
Если есть средства для предварительного чтения, то можно что-то выжать,
если нет --- надо переходить на ОС, где есть.
---
"Истина грядёт --- её ничто не остановит!"
так вот очень часто по хедеру ясно, что тело пакета по сути не нужно и хочется его быстро пропустить.кстати, если считывание информации последовательное (т.е. без возвратов назад то использование seekg неуместно - надо использовать skip.
однако оказалось, что .seekg(f,PacketLength,ios::cur) жрет больше времени, чем .read(tmpbuffer, PacketLength).
Один чёрт, от библиотеки и операционной системы сильно зависит.согласен конечно
я об этом тоже писал
Но зависит не то будет или нет выигрыш - он будет почти всегда (особенно если буферизацию fstream отменить а то какой будет выигрыш и стоит ли ради него парится.
В любом случае, если заморочки с собственной буферизацией, эффективной для данной задачи, геморнее использования стандартной библиотеки, или ухудшают читабельность и логичность кода, или этот участок кода не является очень критичным для общей производительности, то не стоит парится.
Это совершенно другой вопрос.
Вообще-то странно, что никто не сказал про виндовые средства наподобие aio.
---
...Я работаю антинаучным аферистом...
Там, где заведомо нужен только последовательный доступ, мапинги не рулят.Насколько я знаю, копирование файлов через read/write и через mmap дает практически одинаковую скорость.
Спорный вопрос, на линуксе по крайней мере mmap и read рулят с переменным успехом.
Правда, сам тесты не запускал, читал lkml только.
Надеюсь не надо объяснять где аж дважды последовательно происходит доступ к памяти во время выполнения каждой из команд call и ret? (причем один из этих двух доступов вероятно не в кеше)Наглая ложь(с).
Во-первых, в рассматриваемом случае в кэше окажется вообще всё - и стек, и код вызывающей функции, и код вызываемой, и локальные переменные обеих. И call будет исполняться за один такт. Причём безусловный call даже не будет скидывать конвеер.
Во-вторых, задержки по доступу к винту превышают возможные задержки на вызовах функций эдак на шесть десятичных порядков. В миллион раз, то есть. Миллисекунды против наносекунд. И есть некоторая надежда, что выставление правильного размера буфера непосредственно у файла позволит оси оптимизировать именно эти, большие задержки. Например, будет более правильно использоваться внутренний буфер винта.
сделал тест:
без всяких обработок считал файл 1.7 Г блоками 200 байт - 2.5 минуты
затем блоками в 30Мб - 6 секунд.
сделал надстройку над истримом: чтобы через буфер 30Мб все делалось - ускорения никакого (в смысле полная читалка файла с обработкой значений- 4-5 минут все равно занимает)
ничего не понимаю=(
пакеты в файле имеют размеры от 4 байт до 200 в среднем
читается файл дважды последовательно
280 мегабайт в секунду? Молодой человек, вы что-то не договариваете. Таких винтов небывает.
оказывается все зависит от последовательности проверки.
если сначала по 200 читать, а потом по 30 Мб, то такой результат:
DBG: istream_read by 200 bites:162.556; 8607427 blocks readed
DBG: istream_read by 30Mbites bites:2.62639; 58 blocks readed
если же переставить, то такой:
DBG: istream_read by 30Mbites bites:152.937; 58 blocks readed
DBG: istream_read by 200 bites:2.57991; 8607427 blocks readed
вот так проверка делается:
char buf[30000001];
InStream.clear;
InStream.seekg(0);
h=GET_CURRENT_TIME;
int kk = 0;
do
{
istream_read(&InStream, buf, 30000000);
kk++;
}
while (InStream.rdstate == ios::goodbit );
cout <<"\nDBG: istream_read by 30Mbites bites:"<< GET_CURRENT_TIME-h << "; "<<kk<<" blocks readed";
cout.flush;
InStream.clear;
InStream.seekg(0);
h=GET_CURRENT_TIME;
kk = 0;
do {
kk++;
istream_read(&InStream, buf, 200);
}
while (InStream.rdstate == ios::goodbit );
cout <<"\nDBG: istream_read by 200 bites:"<< GET_CURRENT_TIME-h<< "; "<<kk<<" blocks readed";
cout.flush;
зы: мегабайт в этом топике равен 1 миллион байт
зы2: время выводится в секундах
слишком долго считывается...
нужен маневр
по задаче - ты файл обрабатываешь один раз, или много раз?
читалка делает два прохода по файлу и создает внутреннюю структуру данных.
далее все в памяти происходит.
вопрос на самом деле такой: может быть можно заранее один раз построить индексный файл? и дальше уже пользоваться им?
этот метод отпадает.
нужно ли попробывать все на FILE* сделать?
есть ли вероятность, что fread даст лучшие результаты?
осталось допетрить как
зы: хотя если они используют хорошую внутренную структуру данных, то им требуется одно прочтение файла...а это хороший выигрыш, но внутр структуру я не меняю и смирился с ней...
надо ускориться как-то ;(
маза это всё меняет
Тред не осилил. Порекомендовал бы вынести информацию о блоках (те заголовки по 4 байта) один раз вынести в отдельный файл кэша, а потом использовать по необходимости этот кэш.
какие есть советы по этому поводу?:)
могу попробывать на локальном диске тест прогнать
уже однажды гонял: 2.5 минуты считывания вместо 4-5
под линукс xeon.
> 280 мегабайт в секунду? Молодой человек, вы что-то не договариваете. Таких винтов небывает.
У него NFS. Так что там столько слоёв кэширования, что со счёту собьешься их считать.
хм
уменьшать количество удалённых запросов
нормальный совет?
плохой
как ускорить эти 2.5 минуты?
Ты можешь привести не фрагмент тестовой программы, а программу полностью? Ту самую, которая блоками по 200 байт читает 1.7Гб в течение 2.5 минут.
интересно, несколько потоков замедлят доступ или ускорят..
Вряд ли. Ускорить может aio (кстати уже упоминалось в этом треде). Например в FreeBSD aio выполняется в отдельном ядерном треде, не зависимом от данного процесса. Возможно в линукс так же.
filebuf fb;
fb.open(filename,ios::read|ios::binary)
istream InStream(&fb);
я бы прогнал тестик...
тредами не пользуемся вообще ;(
хотя и пускаем на 4-8 процессорных тачках обычно.
причем проверять стоит со сброшенным кэшом (сначала запускаешь чтение каких-нибудь других больших файлов)
очередной бесполезный тред.
можно в мусорку скидывать.
Извини, я в этом языке ничего не понимаю. Мне нужна рабочая тестовая программка, которая просто компилится и работает.
Вот такую программку сделал я:
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 200
int
main(int argc, char **argv)
{
char buf[BUFSIZE];
int nread, fd;
if (argc < 2)
errx(1, "argument required");
if fd = open(argv[1], O_RDONLY < 1)
err(1, "can't open %s", argv[1]);
while nread = read(fd, buf, BUFSIZE > 0)
;
if (nread != 0)
err(1, "read");
}
Локально она читает файл размером 1461585920 за 39.5 секунд, получается 37 Мбайт в секунду, что вполне похоже на скорость этого винта. То есть, несмотря на 200 байтовый буфер, скорость близка к скорости железа. Тестовый файл не читался ранее (с момента загрузки машины то есть кэширование исключено. Винт SATA, машина Athlon XP. Насколько понял я, у тебя более мощные машины.
Прямо сейчас у меня нет NFS сервера и клиента, между которыми гигабит, поэтому протестировать на NFS могу завтра, если тебе интересно.
P.S. Речь о операционной система FreeBSD.
я уже спросил, как еще можно считывать файл
я могу попробывать read делать без стримов на линуксе Xeon+nfs.
да и на локальном могу протестить.
ты пользуешься другими функциями чтения файла.Твой плюсовый fread внутри себя тоже делает read(2). Любой высокоуровневый интерфейс в UNIX, который читает файлы, в глубине себя заканчивается или read(2) или mmap(2).
я уже спросил, как еще можно считывать файл
я могу попробывать read делать без стримов на линуксе Xeon+nfs.
да и на локальном могу протестить.
Или aio_read...
fread делает read значит?....
ща протестю read
он работает с ним через свой буфер
а) нфс: 94 сек и по 1.5 сек на остальные два теста
б) локально: 34 сек и по 2.5 сек на остальные.
Твой плюсовый fread внутри себя тоже делает read(2).мама родная
fread - не плюсовый, это функция стандартной библиотеки C (ну и конечно в плюсах он тоже есть в качестве рудимента).
Наглая ложь(с).во-первых: где ложь? Я что где-то писал, что все что надо не может быть закешировано?
Во-первых, в рассматриваемом случае в кэше окажется вообще всё - и стек, и код вызывающей функции, и код вызываемой, и локальные переменные обеих. И call будет исполняться за один такт. Причём безусловный call даже не будет скидывать конвеер.
Во-вторых: когда приводишь тирады в этом духе и разного рода сильные заявления типа "call будет исполняться за один такт", надо указывать как минимум ядро проца к которому это относится и ситуацию, в которой это верно. Уж не будешь ли ты утверждать, что всякий call выполняется за один такт? Как минимум вызываемую функцию когда-то надо закешировать впервые (что может конечно произойти и заранее параллельно выполнению, а может и не произойти, например потому что текущее выполнение осуществляет доступ к памяти, занимая шину). А что если очень часто происходят call-ы функций раскиданных по всей оперативке? Тоже все и всегда за 1 такт? Гы гы.
Во-вторых, задержки по доступу к винту превышают возможные задержки на вызовах функций эдак на шесть десятичных порядков. В миллион раз, то есть. Миллисекунды против наносекунд. И есть некоторая надежда, что выставление правильного размера буфера непосредственно у файла позволит оси оптимизировать именно эти, большие задержки. Например, будет более правильно использоваться внутренний буфер винта.это все понятно.
но к чему это вообще написано?
Тонкости реализации кеширования ядром ОС доступа к устройствам мы вроде не обсуждали в этом треде.
fread делает read значит?....это правда что ли открытие?
ща протестю read
read - это системный вызов
ясно, что все функции доступа к файловой системе работают через системные вызовы
Разница в том, что fstream (который тоже работает через read/write/прочее на юниксах) - это средство стандартной библиотеки C++, а значит оно работать будет везде, а не только на юниксе, как системные вызовы юникса.
Тоже касается и fread который есть функция стандартной библиотеки C. fread - это C-функция, и использовать ее в C++ проге - полное извращение.
FILE (C) и fstream(C++) имеют еще внутреннюю буферизацию (которую можно отключить) - не путать с кешированием системой ввода-вывода ОС.
Если использовать собственную буферизацию, то библиотечную буферизацию (FILE/fstream) разумно отключить.
А вообще насколько я понял у тебя узким местом является доступ к файлу, так что можно особо не заморачиваться над оптимизацией использования ресурсов процессора.
PS: если хочешь оптимальной производительности, не жертвуя портируемостью, то пиши целиком на C, используя стандартную библиотеку (там где ее достаточно). А если пишешь на плюсах, то пиши наиболее логично и читабельно и забей на эти измерения.
я вставил подобный тест в самом начале своей тестовой функции ( с BUFSIZE 30Mb)Чёрт возьми, тебя тяжело понять. Какой подобный тест? Просто покажи свою программу в таком виде, какую ты использовал. Проще показать код, чем описывать внесённые изменения словами.
а) нфс: 94 сек и по 1.5 сек на остальные два теста
б) локально: 34 сек и по 2.5 сек на остальные.
Да и в результатах тоже лучше приводить не только время, но и размер считанного файла. И очень важно читался ли файл до этого с момента ребута или нет.
1) считывание истримом с буфером 200 байт
2) считывание истримом с буфером 30Мбайт
3) считывание твоим способом с помощью read буфером в 30Мбайт
для первых двух тестов я код написал. для третьего ты написал.
судя по выводу:
DBG: istream_read by 30Mbites bites:152.937; 58 blocks readed
размер файла 30 миллионов байт* 58 = 1740 миллионов байт (+/- 30Мбайт, последний блок имеет неполную длину).
более точно можно посчитать отсюда:
DBG: istream_read by 200 bites:162.556; 8607427 blocks readed
файл считывался много раз, однако я заметил, что при перезапуске всей проги кеш как будто очищается и первое чтение долго делается, остальные два быстро (я же написал вывод проги и времена).
линуксы и солярки у нас не перегружают. сам понимаешь.
зы: сорри фор май инглиш
нет слова readed =)
да и вообще:
с Наступающим Новым Годом!
всех)
Ну получается 10 Мб/с. У тебя NFS ходит по 100Мбитной сети? Вот ты и упёрся в её максимум.
Оставить комментарий
Maurog
имеется файл размеры порядка 2 Гтам лежат пакеты - блоки инфы с хедером в 4 байта (в них тип инфы и размер следующего блока)
так вот очень часто по хедеру ясно, что тело пакета по сути не нужно и хочется его быстро пропустить.
однако оказалось, что .seekg(f,PacketLength,ios::cur) жрет больше времени, чем .read(tmpbuffer, PacketLength).
то есть по сути быстрее все же перелопатить файл, чем грамотно его обпрыгать
как такое могло выйти?
должно ли так быть?
файл открывается типа
все под линуксом, соляркой и на НФС
есть ли другие методы быстро обпрыгать файл?