Параллельная запись в несколько (десятков) файлов

Тема в разделе "WASM.WIN32", создана пользователем _DEN_, 13 май 2010.

  1. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    _DEN_
    Вообще, насколько я понимаю причины появления std::ofstream в STL, то первая и основная задача которая возлагается на него -- это буферизация i/o. Чтобы не сисколлить из-за каждого байта. (одна из причин, кстати, почему в *nix нах никому не нужен asm: к программе на asm надо написать аналог stdio.h, иначе она будет безумно тормозить на вводе-выводе). Я хз что там в вендовой реализации -- может в венде, ofstream оставляет буферизацию win32api уровню, но, почему-то мне кажется, что это не так. Думается мне, что там примерно то же что и в *nix'ах: заполняем буфер, и, по-мере заполнения сбрасываем буфер на диск. И кажется мне, что std::ofstream просто не может не иметь в своём арсенале методов управления стратегией буферизации. Ты попробуй ему буфер увеличить, и убедись что буферизация типа _IOFBF (Full), а не _IOLBF (Line) или ещё хуже _IONBF (No).
    Если же с буферизацией всё норм, то я осмелюсь предположить, что проблема в убогости архитектуры заложенной в ofstream и другие подобные библиотеки типа stdio.h. Все они подразумевают лишнее копирование памяти: из твоего буфера, в свой внутренний буфер. Сокеты ты ведь небось тоже оборачиваешь во что-то типа std::stream? Вот посчитай: ядро получило данные из сетевухи и вытащило их из стека tcp/ip. Дальше ядро копирует в буфер istream сокета, потом istream копирует эти данные в твой буфер, потом ofstream копирует из твоего буфера в свой внутренний, а потом ядро копирует из этого внутреннего буфера ofstream в адресное пространство ядра. Помножь битрейт на четыре копирования, а потом ещё на 16 потоков, и ты получишь примерную оценку того, с какой скоростью проц должен уметь выполнять memcpy. Копирования из/в ядерное АП необходимы, и от них никуда не деться (хотя mmap на файле/сокете может помочь в этом), а вот два копирования происходящих в адресном пространстве процесса, смотрятся как явный оверхед.

    Асинхронный же ввод/вывод, думается мне не поможет. Если бы ты ввод/вывод 16-ти сокетов + 16-ти файлов честно реализовывал бы в одном потоке, то тогда было бы о чём подумать. А так -- асинхронный ввод вывод лишь изменит место, в котором твой поток будет дожидаться окончания операции ввода/вывода. Так он блокируется на read или write, а с асинхронным вводом выводом, он явно будет вызывать функцию типа "жду_когда_будет_завершена_какая_нибудь_из_aio_операций". Я не думаю, что от этого что-то существенно изменится.
     
  2. assasincore

    assasincore New Member

    Публикаций:
    0
    Регистрация:
    7 апр 2010
    Сообщения:
    55
    _DEN_
    Лол, иди учись кодить, а не паттерны готовые "брать", при чем не понимаю что это, чего это стоит ...
     
  3. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Booster
    Формулировка конечно кривоватая, но зачем к ней придираться? и так же понятно что это переводится как "достаточно большую нагрузку, которая заметно больше, чем у сторонних приложений с аналогичной функциональностью" :))

    Сейчас подумал что в этом случае тормозов по сравнению с синхронным вариантом записи может и не быть поскольку речь идёт о сериализации записи на уровне одного потока, что гораздо проще чем сериализировать запись из нескольких потоков/задач и возможно винда с этим справится адекватно.
    Но вариант с очередью на основе общей переменной всё равно будет проще и в разработке и в сопровождении.

    r90
    Если запись уверенно успевает закончится до того как будет готов следующий фрейм то ты прав от перехода к асинхронной записи ничего не изменится, но если запись тормозит, а это может быть спровоцировано как борьбой потоков _DEN_а между собой (если не принимать мер по сериализации), так и интенсивной загрузкой диска сторонним приложением то конфликт двух синхронных операций - приёма и записи данных вполне возможен.
    А тормоза тут имхо как раз в дебрях win (реализация многопоточной сериализации записи) и STL не виновата.
     
  4. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    А вообще (извините конечно за оффтоп, но не могу удержаться), мне честно говоря не очень понятно, как в приложении критичном к пропускной способности i/o можно доверять user-space буферизацию библиотекам общего назначения. Тут явным образом напрашивается расписать main-loop на старом добром C. Не используя ничего кроме сисколлов. Да и от потоков, я бы пожалуй отказался бы: я конечно понимаю, что многоядерность ныне повсеместна, но при правильном подходе, процесс будет большую часть времени "спать", иногда просыпаясь и выдавая ядру очередную пачку указаний. По-крайней мере так оно будет в *nix'ах. Так что многоядерность процессу без надобности, а вот 15 лишних потоков -- это не более чем лишняя работа ядерному шедулеру задач.
    А для каждой ОС написать свой main-loop не так уж и страшно как кажется. Тем более что разных ОС буквально раз, два и обчёлся: windows и *nix. MacOS я не видел, но читал что она на BSD ядре, так что надо предполагать, что там подход к i/o стандартный.

    Кстати подумав, я всё же склоняюсь к мысли что проблема в слишком мелком буфере ofstream. Я думаю там по умолчанию что-то типа как в FILE из stdio.h, то есть 4Kb. А это ничто. Мои эксперименты показали что буфер надо иметь размером порядка 1Mb. Хотя эти эксперименты я проводил давно, у меня тогда PIII стоял 1GHz частотой, и проводил я их под линуксом. Но я не думаю, что от использования windows и Core2Quad оптимальный размер буфера может уменьшится до нескольких килобайт.
     
  5. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Y_Mur
    Я подумал, что функциональность у приложения выше чем у аналогов и соответственно нагрузка большая ^)
     
  6. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    r90
    Если моя версия что win не эффективно сериализириует запись при наличии нескольких одновременных запросов от нескольких потоков/приложений верна, то переход к прямому использованию АПИ ничего не даст.

     
  7. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Y_Mur
    Я какбэ не специалист по вводу-выводу в windows. Все свои знания я подчерпнул в linux'е, разглядывая чужие программы, ковыряясь в ядре, создавая свои программы и читая статейки других исследователей. Ввод-вывод windows же меня не интересовал никогда. Я слышал конечно, что в windows безумно убогая дисковая подсистема тормозящая при любом удобном случае. Но всё же, я не очень верю в то, что дисковая система может быть убога настолько, насколько описываешь ты. Я всё же верю в то, что в MS не лохи работают. Ну не настолько же они лохи, чтобы не догадаться до того, чтобы заглянуть в linux и выяснить как надо буферизировать ввод-вывод в ядре?
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    r90
    Просто попробуй примитивнейший эсперимент - запусти в win копирование большого файла (или каталога), посмотри как бежит прогресс-бар копирования и на время прогноза завершения, а потом не дожидась пока это копирование завершится начни копировать ещё 1-3 больших файлов или каталогов. Сразу увидишь как прогресс-бары у всех глухо замирают, а время прогноза завершения операции увеличивается далеко не пропорционально количеству копируемых файлов и да и привирать этот прогноз сразу начинает безбожно, разумеется занижая реальное время :)
     
  9. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Вообще прежде чем рассуждать, необходимо сделать предварительные прикидки. Сколько приходит данных, какова пропускная способность дисковой системы, что понимается под нагрузкой. Может проблема вообще в другом.
     
  10. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Y_Mur
    Копирование здесь не совсем подходит. В сабже только запись, согласись это далеко не тоже самое.
     
  11. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Y_Mur
    Более "примитивного" ты не мог придумать? Где я win возьму? Могу под wine'ом попробовать, но я не думаю, что результаты будут как ты описываешь.

    Но вообще, если отталкиваться от твоего описания, то я могу найти и другое объяснение этому: жёсткому диску слишком много приходится трясти головками. Если бы этот эксперимент был бы проделан в виде копирования /dev/zero в новые файлы, то тогда, пожалуй, твоё объяснение было бы безупречным. А так нельзя игнорировать тот факт, что одновременно читаются разные файлы. Запись она для ядра попроще, меньше напрягает жёсткий диск, поскольку драйвер фс может выбирать свободное место для записи минимизируя движения головок жёсткого диска.
    Кроме того, пример неудачен, поскольку предполагает использование explorer.exe -- эта программа, по-моему, слишком сложна для того, чтобы ориентируясь на её поведение, изучать особенности поведения vfs. explorer.exe, я думаю, привносит в измерения существенные помехи, которые нельзя игнорировать. Такие тесты надо проводить используя dd: no GUI, простой код заточенный на эффективность пересылки данных из одного источника (/dev/zero) в другой (/tmp/test.file), плюс конфигурабельность, в частности возможность выбрать размер используемого user-space буфера.

    А вообще, мне интереснее другое. Чем с точки зрения ядра отличается асинхронный write, от write синхронного? Мне всегда казалось, что существенная разница видна лишь процессу который использует write. На синхронном write процесс будет заблокирован, до тех пор, пока ядро не пристроит данные переданные в буфере. Асинхронный же гарантированно к блокированию не приведёт, процесс продолжит работу, а ядро точно так же будет выискивать способы впихнуть куда-то данные. Конечно же, ядру придётся поступать с буфером несколько иначе, но на мой взгляд, это "иначе" носит больше косметический характер.
     
  12. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    r90
    Ну тогда поверь мне на слово - проверял от 95 до XP :), кстати проверь на никсе (которого у меня по рукой нет) - для меня удивительно неужели там и вправду нет этой заморочки?

    Ты совершенно прав синхронная и асинхронная запись существенно отличаются только с точки зрения вызвавшего их потка.
    Но когда два и более потока/приложения одновременно требуют синхронную или асинхронную запись то похоже винда переключает их как обычные задачи (вместо того чтобы всё правильно забуферизировать), что при интенсином использовании диска, как общего ресурса, приводит к резкому повышению накладных расходов, которые кстати не сводятся к позиционированию головок - у флешки никаких головок нет, а на ней описанный выше эффект ещё явственней чем на HD :))
     
  13. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Винда слабо кэширует, это видно на примере флешек, если записать и вытащить, то данные останутся. На линуксе без отмонтирования, фиг с маслом.
     
  14. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Y_Mur
    Несколько оффтопично, но всё же выложу результаты. Итак, тест #1, 10 процессов dd, каждый копировал 1000 кусков по одному мегабайту каждый из /dev/zero в test-file-$i, где i -- это номер этого самого dd. Суммарный объём копирования я взял 10Gb, поскольку у меня 4Gb оперативки, и её неплохо было бы забить в процессе теста, чтобы весь тест был бы работой с диском, а не memcpy между user- и kernel-space.
    Код (Text):
    1. for ((i=0; i<10; i++)); do echo $i started; time dd if=/dev/zero of=./test-file-$i bs=1M count=1000& ibs=1M; echo $i ends; done
    2. [...]
    3. 1000+0 записей считано
    4. 1000+0 записей написано
    5.  скопировано 1048576000 байт (1,0 GB), 121,912 c, 8,6 MB/c
    6.  
    7. real    2m1.927s
    8. user    0m0.001s
    9. sys 0m3.142s
    10. [...]
    Я выкинул результаты всех процессов, кроме одного, поскольку они очень похоже. Оставил результаты последнего завершившегося.

    Тест #2. Один dd, копирующий 10.000 кусков по 1Mb каждый. Тот же самый объём, но в один файл, одним потоком.
    Код (Text):
    1. time dd if=/dev/zero of=./test-file bs=1M count=10000
    2. 10000+0 записей считано
    3. 10000+0 записей написано
    4.  скопировано 10485760000 байт (10 GB), 119,132 c, 88,0 MB/c
    5.  
    6. real    1m59.138s
    7. user    0m0.029s
    8. sys 0m31.852s
    Реально затраченное на копировение время (real) чуть уменьшилось, но в пределах погрешности измерений. Время проведённое в user-space очень похоже на сумму времён тех десяти процессов (в них оно колусталось от 0.001 до 0.004), время же ядра совпадает в двух знаках.
     
  15. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Booster
    Такова политика по умолчанию для флешек. Ничто не мешает это изменить (слева дефолтный хард, справа дефолтная флешка).

    Y_Mur
    До nt6 размер IO-реквеста имел верхнее ограничение в 64k. Эта ситуация с множественным копированием и explorer'ом значительно изменилась с тех пор. (Но ни в коем случае не проверяй это на vist'е :)

    _DEN_
    Решение представленной задачи, вероятно, стоит начинать с профилирования – какой смысл знать о "большой нагрузке", если не известен её зумированный источник?
     
  16. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    В общем сделал асинхронную запись на Boost.Asio, сериализованно в один поток большими блоками. Скоро будет тест перфоманса, посмотрим что из этого получилось.
     
  17. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Итак, вернемся к нашим барашкам.

    Результаты оказались очень даже хорошими. 16 стримов, пишущихся на винт в 16 потоках, при том что на каждый кадр происходил WriteFile, при 30 FPS и еще ..дцать кадров в секунду на звук, давали загрузку процессора... уже не помню точно, но точно что более 50%. После того как я все переписал на однопоточную сериализованную отложенную буфферизованную запись через Boost.Asio, загрузка в той же ситуации упала до нескольких процентов.