Стратегии ввода / вывода для вычислительных задач с большими наборами данных?

15

Моя исследовательская группа фокусируется на молекулярной динамике, которая, очевидно, может генерировать гигабайты данных как часть одной траектории, которую затем необходимо проанализировать.

Некоторые из проблем, с которыми мы сталкиваемся, связаны с корреляциями в наборе данных, что означает, что нам нужно отслеживать большие объемы данных в памяти и анализировать их, а не использовать более последовательный подход.

Я хотел бы знать, каковы наиболее эффективные стратегии обработки ввода-вывода больших наборов данных в сценарии. Мы обычно используем скрипты на основе Python, потому что это делает кодирование файлового ввода-вывода гораздо менее болезненным, чем C или Fortran, но когда у нас есть десятки или сотни миллионов строк, которые необходимо обработать, не очень понятно, какой подход лучше , Должны ли мы рассмотреть вопрос о том, чтобы сделать часть кода для ввода файлов в C, или другая стратегия более полезна? (Будет ли простая предварительная загрузка всего массива в память лучше, чем серия последовательных чтений «кусков» (порядка мегабайт)?

Некоторые дополнительные заметки:

  • В первую очередь мы ищем инструменты сценариев для постобработки, а не инструменты «он-лайн» - отсюда и использование Python.

  • Dзнак равно16ИтΔT(Икс(T+ΔT)-Икс(T))2
aeismail
источник

Ответы:

6

Я предполагаю, что ваш вопрос связан с наблюдением, что ввод-вывод приводит к значительным накладным расходам во всем анализе. В этом случае вы можете попытаться перекрыть ввод-вывод с вычислением.

Успешный подход зависит от того, как вы получаете доступ к данным, и от вычислений, которые вы выполняете для этих данных. Если вы можете определить шаблон или доступ к различным областям данных известен заранее, вы можете попытаться предварительно выбрать «следующие фрагменты» данных в фоновом режиме при обработке «текущих фрагментов».

В качестве простого примера, если вы просматриваете файл один раз и обрабатываете каждую строку или набор строк, вы можете разделить поток на куски строк (или МБ). Затем на каждой итерации блоков вы можете загрузить блок i + 1 во время обработки фрагмента i.

Ваша ситуация может быть более сложной и требует более сложных решений. В любом случае, идея состоит в том, чтобы выполнить ввод / вывод в фоновом режиме, пока у процессора есть некоторые данные для работы. Если вы дадите более подробную информацию о вашей конкретной проблеме, возможно, мы сможем глубже изучить ее;)

---- Расширенная версия после предоставления более подробной информации ----

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

  • если процент ввода / вывода низкий (низкий, так как у вас нет никакого дела до издержек, независимо от того, что это: 0,5%, 2%, 5%, ...), тогда просто используйте простой подход: загрузка данных сразу и вычисляй. Вы сэкономите время для более интересных аспектов вашего исследования.

  • если вы не можете позволить себе накладные расходы, вы можете посмотреть, что предложил Педро. Имейте в виду, что упомянул Арон Ахмадиа, и протестируйте его, прежде чем приступить к полной реализации.

  • если предыдущие не являются удовлетворительными, я бы пошел на некоторую неосновную реализацию [1]. Поскольку кажется, что вы выполняете вычислений для данных, есть надежда :) Некоторый псевдокод (при условии, что результаты вашего анализа помещаются в ОЗУ):N2N

    загрузить chunk1 и chunk2
    для кусков я = 1 до п
        асинхронная загрузка чанка i + 1
        для кусков в j = i + 1 к n
            асинхронная загрузка фрагмента j + 1
            вычислить с чанками i, j (* для первой итерации, это предварительно загруженные чанки 1 и 2 *)

Примечание: это быстрый и грязный псевдокод, необходимо настроить индексы.

Для реализации этого обычно используют так называемую двойную буферизацию . Грубо говоря: разделить память на две рабочие области; в то время как данные загружаются в фоновом режиме в рабочую область 1, процессор выполняет вычисления с данными в рабочей области 2. На каждой итерации меняйте роль.

Извините, я не могу придумать хорошую ссылку прямо сейчас.

[1] Внеядерный алгоритм включает в себя некоторый механизм для (эффективной) обработки данных, находящихся на диске. Они называются вне ядра, а не внутри ядра («в оперативной памяти»).

Диего
источник
7

Раньше мне приходилось сталкиваться с подобными проблемами, и мое любимое решение - использовать отображаемый в память ввод-вывод , хотя и в C ...

Принцип, лежащий в основе этого, довольно прост: вместо того, чтобы открывать файл и читать из него, вы загружаете его непосредственно в память и получаете к нему доступ, как если бы это был огромный массив. Уловка, которая делает его эффективным, заключается в том, что операционная система на самом деле не загружает файл , а просто обрабатывает его как выгруженную память, которую нужно загрузить. Когда вы получаете доступ к любому данному байту в вашем файле, страница памяти для этой части файла помещается в память. Если вы продолжаете получать доступ к различным частям файла, и память становится тесной, менее используемые части будут выгружены обратно - автоматически!

Быстрый поиск в Google говорит мне, что это также доступно для Python: 16.7. mmap - поддержка отображаемых в память файлов , но я не знаю достаточно о Python, чтобы сказать, действительно ли это то же самое.

Pedro
источник
1
Просто убедитесь, что вы измеряете и тестируете, прежде чем внедрять что-то вроде mmapосновного кода. Многие современные операционные системы дают аналогичную производительность между обычными readс меньшими сложностями. (Кроме того, да, mmap в Python предоставляет переносимый интерфейс для карт памяти Windows и UNIX).
Арон Ахмадиа
1

Возможно, вы можете использовать Cython в своих разделах ввода-вывода и конвертировать эту часть в C-код?

asmatic
источник