C++ streams compatibility...
lexa - 05/Окт/2010 18:58
Граждане программисты,
Я - человек темный, граблями причесываюсь на фортране-77 все еще программирую. Но мне пишут, что дескать в современных C++-стримах все сделано по уму в смысле буферизации и работают они временами сильно быстрее, чем любимый мой FILE*
Собственно, сомневаться причин нет, в тестах так оно и получилось (и разница заметная), но мучает меня вопрос с совместимостью.
Мою LibRaw на чем только не собирают, вот даже на Maemo, а давеча я проблемы с Visual Studio 2003 правил.
Отсюда вопросы (сам я за всем этим не слежу, проще спросить):
- Интерфейс то стримовый за последние лет 7-10 - он вообще как, стабилизировался?
- Следует ли ожидать всяких открытий чудных, вроде того что метод есть, но не работает?
- С Linux/Mac проблем нет, я вижу что в gcc 4.x все (на первый взгляд) нормально. А что с виндами, причем как в ипостаси Visual Studio, так и cygwin/MinGW? Может есть какая-то табличка по совместимости хотя бы по Visual C++
Comments
Мы под себя stream адаптировали (подменяли std::basic_filebu
Мы под себя stream адаптировали (подменяли std::basic_filebuf), граблями по лбу не поймали. Правда, мы и под экзотику всякую не компилируем, только MSVC 2005 и старше да GCC 4.x под Linux и Mac OS X.
Ну насколько я понимаю, вы компилируете всегда сами, не опен
Ну насколько я понимаю, вы компилируете всегда сами, не опенсорс....
Буду пробовать, но ожидаю открытий чудных....
Я - тоже человек темный, тестов не читал. При своем очень не
Я - тоже человек темный, тестов не читал. При своем очень немаленьком опыте разработки на C++ в превосходство *производительности* работы std-шных потоков над операциями через FILE* не особо верю, там другие плюшки - в основном типизированный и безопасный ввод-вывод.
Буферизация ввода-вывода - вещь довольно банальная, сильно улучшить её на потоковых операциях крайне затруднительно. Так что единственный реальный ресурс ускорения потоковых операций над FILE* - это переход с вызовов функций на подстановку inline-ов. В каких-то, особенно патологических случаях (тестах) может сыграть.
Насколько я вижу, в FILE* имеются лишние локи. В результате
Насколько я вижу, в FILE* имеются лишние локи. В результате у меня жалкий 37-мегабайтный файлик fgetc()-ем читается (т.е. посимвольно) почти две секунды. Про getc_unlocked я знаю.
Не, что фишка быстрее работает - видно в тестах. Не на всех системах, вот на FreeBSD совсем уж лишних локов нет и разницы тоже почти нет.
Да, "лишние локи" - под Linux, если собирать мультитредную п
Да, "лишние локи" - под Linux, если собирать мультитредную программу или с -fopenmp (которая тоже мультитредной становится).
Без этого - все хорошо, значимой разницы не вижу. Но openmp мне надо, от него счастье.
На счёт OpenMP - насколько я
На счёт OpenMP - насколько я помню в VS он появился с 2005 (проверял на 2005,2008,2010).
Но, надо учесть что редакции Standard не поддерживают из коробки OpenMP. В компиляторе поддержка есть, но openmp либы(и другой stuff) не поставляются(можно просто скопировать из Professional редакции(проверено). Также где-то читал, что нужные файлы есть в Windows SDK, но я не проверял).
Когда появился в gcc не уверен, но проверял на gcc34 и более новых - полёт нормальный.
Если необходима поддержка как можно большего числа компиляторов и сред, от OpenMP придётся отказаться в пользу какой-нибудь другой либы.
(конечно там где OpenMP не поддерживается, не трудно за-ifdef'ить код OpenMP
(тем более при использовании OpenMP этого кода не так много (прагмы можно не глушить)),
но тогда пропадает многопоточность )
Не, ну естественно, там где
Не, ну естественно, там где OpenMP не поддерживается, там без него. Без него тоже работает
буферизацию надо свою. независимо. и инлайнить.
буферизацию надо свою.
независимо.
и инлайнить.
А более правильных вызовов, типа fread(дофига) религия испол
А более правильных вызовов, типа fread(дофига) религия использовать не позволяет? Жалкие 37 метров одним вызовом усосать можно, или есть какие-то очень странные ограничения по памяти?
fgetc() вообще штука крайне специфическая. Обычно стараюсь э
fgetc() вообще штука крайне специфическая.
Обычно стараюсь этим вызовом не пользоваться, благо ему есть простая замена - fread() + цикл по буферу.
Очень не хочется переписывать почти полный набор примитивов
Очень не хочется переписывать почти полный набор примитивов FILE* самостоятельно. Велосипед же ж.
Переписать "это место" руки чешутся, конечно, но очень хочет
Переписать "это место" руки чешутся, конечно, но очень хочется обойтись без этого....
Сорри, я тёмный, а что там в
Сорри, я тёмный, а что там в LibRAW такого, кроме чтения и записи файлов? Оно ж сразу целиком в память читается одним куском, не? Да и писать тоже можно сразу блоками, типа writev. К чему там буфера, собственно?
Увы. Код распаковщика
!(reset = zero_after_ff && c == 0xff && fgetc(ifp))) {
bitbuf = (bitbuf << 8) + (uchar) c;
vbits += 8;
}
{
CHK();
return substream?substream->get_char():fgetc(f);
}
А использовать mmap() в таком
А использовать mmap() в таком месте не проще? Оно и буферизоваться будет по 4 кб замечательно.
Правда под виндой оно несколько иначе вызывается.
Ну у меня естественно есть
Ну у меня естественно есть интерфейс поверх буфера в памяти, где fgetc развернется в buf[bufp++]
А буфер можно и mmap-ом получить.
Но бывают очень большие файлы, которые не замэпать. Не говоря о всяких maemo и прочих мобильных девайсах, где памяти мало и есть ли там mmap - тоже вопрос.
Дошли руки это место
Дошли руки это место побенчмаркать.
FreeBSD, single thread, файл на 20+ мегабайт.
mmap() гораздо медленнее, чем файловый доступ. Т.е. на 20-25 мегабайтном файле - лишние миллисекунд 200-300. Это если просто MAP_PRIVATE. MADV_WILLNEED и MAP_NOCORE несколько улучшают.
Интересно. Значит у меня
Интересно. Значит у меня устаревшие воспоминания.
Видимо ему advise не хватает основательно. А вы проверяли на SSD или на обычном диске?
Да, это SSD. На обычном (хм.
Да, это SSD. На обычном (хм. обычном массиве, у меня одиночных HDD нету) тоже померяю.
UPDATE: а диск не должен никак влиять. Файл маленький, кэш большой, все тайминги - несколько запусков, но первый запуск (с поднятием в кэш) не считается.
Ну и конечно мой код, который
Ну и конечно мой код, который с preallocated-буфером работает, может быть не самым оптимальным (зато компактный).
И еще :), раз уж вы читаете
И еще :), раз уж вы читаете комментарии, значит подробности могут быть интересны.
Если код читает по байтам (для FILE это getc_unlocked), то байтовое чтение и чтение через mmap ( return buf[bufp++]) - примерно одинаково по времени (а основное время там уходит в свисток - в распаковку хаффмана, более гранулярно я уже не бенчмаркал). Т.е. fgetc/getc_unlocker действительно сильно неэффективны.
А если код читает кусками (по строкам RAW т.е. несколько килобайт) - то те самые 200-300 миллисекунд разницы на 25 мегабайтах.
распаковка хоффмана - это у
распаковка хоффмана - это у вас ZFS наверное, там много чего может оказаться если цепочка буферов настроена неоптимально. А при работе с SSD - у него вроде большой внутренний размер страницы, кто-то говорил что 64К, и оно может плохо ложиться в маленький буфер файла. Но первичны конечно именно ваши экспериментальные замеры.
Сферически в вакууме, мапирование в виртуальной памяти - это самый естественный и дешевый доступ, поскольку достигается zero-copy и размер окрестности выбирается автоматически исходя из наличия свободной памяти, кроме того о распараллеливании чтения думает вместо нас операционная система (с тем или иным успехом;) А при чтении файла буфера копируются с места на место, проверяются постоянно границы буфера, указатель (который tell/seek) надо постоянно двигать - масса лишней работы.
В яндексе еще больше 10 лет назад индекс читался кусочками с помощью
ptr = mmap((caddr_t)NULL, size, PROT_READ, MAP_SHARED, fd, offset)
а потом сразу munmap(), поскольку целиком файл не умещался в 32bit.
При нескольких словах в запросе и множестве параллельных запросов - оно как-то все распараллеливалось внутри FreeBSD+SCSI вполне замечательно. При явном чтении получалось хуже.
Не знаю как сейчас с mmap64.
Не, вы вероятно не так
Не, вы вероятно не так поняли. То есть ZFS конечно есть, но он на этой машине нежатый.
А речь идет о распаковке RAW-файлов от цифровых фотокамер. Там чужой код (dcraw), под который подложен собственный I/O layer. И я сегодня приделал туда тайминг, чтобы видеть сколько времени занимает собственно распаковка, а сколько - постпроцессинг, а то профайлером это смотреть мучительно.
Так вот, lossless jpeg (который huffman внутри) распаковывается этим чужим кодом с помощью fgetc() в цикле. Который у меня превращается или в getc_unlocked() или в buf[buf++] (во втором случае buf сделан mmap-ом).
А дальше я уже все выше написал, fgetc() в виде fgetc() ничуть не хуже чем buf[buf++], а вот fread() эффективнее чем memmove(). А zero copy я не могу, у меня семантика fread().
С zero copy вполне возможно что будет и эффективнее.
А, да, если Linux и многопоточная программа, то FILE I/O весь в локах (ненужных) и скорее всего mmap выиграет (потому что реально тормозит на локах). И в винде тем более выиграет. А в 8-й FreeBSD локи в этом месте сделаны лучше.
Да, про Хаффмана я
Да, про Хаффмана я погорячился, забыл контекст исходного поста.
У меня кстати обнаружилась красивая задача для поисковика, вам наверное будет любопытно. Попробую написать письмом.
Да, а с чтением одним куском
Да, а с чтением одним куском всего файла - тоже облом. помнится я все файловые офсеты менял на 64-битные. Потому что бывает кино в raw
Я, конечно, ненастоящий
Я, конечно, ненастоящий сварщик, и давно не смотрел на тему, но вроде как стандарт в области потоков давно не трогали?
вопрос в том. нет ли таких
вопрос в том. нет ли таких форм жизни, где это место неживое. Одно я уже нашел, это gcc44 на FreeBSD (допускаю, что у меня порт был криво поставлен, завтра переставлю и перепроверю), но ведь наверное cygwin/MinGW - это тоже вероятный кандидат. Не говоря о всяких Visual C 6
м-м-м, ну шестой вс брать ...
м-м-м, ну шестой вс брать ... неужели им кто-то еще пользуется? :) он же совсем инвалид был с шаблонами и прочим ... да и то как-то с STLport вроде собирали и работало ...
Ну не я же беру. Опенсорс,
Ну не я же беру.
Опенсорс, пользователи бывают всякие....
Ты еще скажи, что у тебя камеры выдают файлики более 100 мет
Ты еще скажи, что у тебя камеры выдают файлики более 100 метров и засосать их одним вызовом - религия не позволяет. Или ты на мобильники ориентируешься?
Во-первых, это чужой код, "под который" я подкладываю свой I
Во-первых, это чужой код, "под который" я подкладываю свой I/O
Во-вторых, видеокамера (выдающая RAW) больше 2 гигов выдает спокойно, а какой там лимит - я не знаю, но 64-битный file offset появился именно поэтому.
Естественно, с такими файлами - покадровая работа.
Я вот тоже темный и FILE*
Я вот тоже темный и FILE* милее всяких цплюсплюсовых наворотов.
По-любому быстрее всего - тянуть файл в память и потом быстро-быстро препарировать.
Что касается больших многокадровых RAW, наверняка ведь кадры друг от друга независимы - закачиваем каждый по отдельности в память и препарируем.
В общем случае это +25% к
В общем случае это +25% к потребной памяти (для 16-битных нежатых raw). Если пользователь такое может позволить себе - то для этого у него имеется отдельный интерфейс на эту тему "читать RAW из буфера в памяти".
Ха, думал фортран - это уже
Ха, думал фортран - это уже история. Ещё в техникуме пятнадцать лет назад о нём вспоминали.