Foreach в R: оптимизировать использование RAM и CPU путем сортировки задач (объектов)? - PullRequest
1 голос
/ 17 января 2020

У меня ~ 200 .Rds наборов данных, над которыми я выполняю различные операции (разные сценарии) в конвейере (из нескольких сценариев). В большинстве этих сценариев я начал с for l oop и обновил до foreach. Моя проблема заключается в том, что объекты набора данных имеют разные размеры (ось x - это размер в мб):

filesizes

, поэтому, если я оптимизирую использование номера ядра (у меня есть 12-ядерный компьютер 16gbRAM в офисе и 16-ядерный компьютер 32gbRAM дома), он будет проходить первые 90 без происшествий, но затем большие файлы объединяются и максимально увеличивают общее распределение ОЗУ (помните, что файлы Rds сжимаются, так что они больше в оперативной памяти, чем на диске, но изменение размера файла, по крайней мере, указывает на проблему). Это заставляет работников обрабатывать sh и, как правило, оставляет от 1 до 3 ядер, работающих через оставшиеся большие файлы (используя .errorhandling = "pass"). Я думаю, что было бы здорово оптимизировать число ядер на основе количества и объема оперативной памяти работников, а также общего объема доступной оперативной памяти, и решил, что другие могут столкнуться с подобной дилеммой и разработал стратегии для решения этой проблемы. Некоторые подходы, о которых я думал, но не пробовал:

Подход 1: сначала l oop или список файлов на диске, возможно, открывая и закрывая их, используйте object.size(), чтобы получить их размеры в ОЗУ отсортировать по величине к наименьшему, разрезать наполовину, перевернуть порядок второй половины и разбросать их: наименьший, самый большой, 2-й наименьший, 2-й наибольший и т. д. c Поэтому 2 работника (или любое четное число) должны работать над «средним» использованием ОЗУ. Тем не менее: работник 1 выполнит свою работу sh быстрее, чем любая другая работа в стеке, а затем go на работу 3, 2-е наименьшее, вероятно, окончание sh, которое действительно очень быстро выполнит задание 4, второе по величине, в то время как работник 2 по-прежнему находится на самом большом уровне, что означает, что в этом задании этот подход предусматривает одновременную обработку машиной двух самых больших объектов ОЗУ, что противоположно тому, что мы хотим.

Подход 2: сортировка объектов по размеру РАМ для каждого объекта, от малого до большого. Начиная с объекта 1, итеративно добавляйте использование ОЗУ для последующих объектов, пока не будет превышен общий номер ядра ОЗУ. Foreach в этой партии. Повторение. Это бы сработало, но требует некоторого запутанного кодирования (вероятно, для l oop оболочки вокруг foreach, который каждый раз пропускает foreach свой список задач?). Кроме того, если есть много задач, которые не будут превышать объем оперативной памяти (в моем примере), процесс пакетного ограничения ядер будет означать, что все 12 или 16 должны быть завершены до запуска следующих 12 или 16, что приводит к неэффективности.

Подход 3: сортировка small-large per 2. Запустите foreach со всеми ядрами. Это будет максимально эффективно обрабатывать маленькие, пока задачи не станут больше, и в этот момент работники начнут набирать sh, уменьшая количество работников, разделяющих оперативную память, и, таким образом, увеличивая вероятность того, что оставшиеся работники смогут продолжить работу. Концептуально это будет означать, что задачи core-1 не будут выполнены, и их необходимо будет повторно запустить, но код прост и должен работать быстро. У меня уже есть код, который проверяет выходной каталог и удаляет задачи из списка заданий, если они уже выполнены, что означает, что я мог бы просто перезапустить этот подход, однако я должен предвидеть дальнейшие потери и, следовательно, потребуется повторный запуск, если я не опущу ядра число.

Подход 4: как 3, но каким-то образом закрыть работника (уменьшите номер ядра) ПЕРЕД назначением задачи, означая, что задача не должна вызывать переполнение ОЗУ и давать сбой, чтобы уменьшить количество работников. Это также означало бы, что нет необходимости перезапускать RStudio.

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

Любые мысли оценены людьми, которые сталкивались с подобными проблемами. Ура!

1 Ответ

1 голос
/ 17 января 2020

Я тоже немного подумал об этом. Моя проблема немного другая, у меня нет cra sh, но есть некоторые замедления из-за перестановки, когда недостаточно ОЗУ.

Вещи, которые могут работать:

  • randomize итерации, так что они примерно равномерно распределены (без необходимости заранее знать время)
  • , аналогично подходу 5, имеют некоторые барьеры (ожидание некоторых рабочих с некоторым временем l oop и Sys.sleep()) хотя недостаточно памяти (например, определяется с помощью пакета {memuse}).

То, что я делаю на практике:

  • всегда сохраняйте результаты итераций в циклах foreach и проверяйте, уже вычислено (файл RDS уже существует)
  • пропустить некоторые итерации, если необходимо
  • перезапустить "интенсивные" итерации, используя меньше ядер
...