Подтвердить что ты не робот

Как улучшить обмен памяти между процессами единорога с Ruby 2.0 на Linux

Ruby 2.0 представляет собой коллекцию сборщиков мусора для копирования на запись. Мои процессы, похоже, не хранят память в течение более чем нескольких минут - кажется, что она shared_dirty в private_dirty довольно быстро.

Некоторым другим удалось добиться успеха:

Эта программа может использоваться для проверки статистики памяти в Linux: https://gist.github.com/kenn/5105061

Конфигурация моего единорога: https://gist.github.com/inspire22/f82c77c0a465f1945305

По какой-то причине мои приложения для единорога, также с preload_app = true, имеют гораздо меньшую общую память. Ruby 2.0-p195, рельсы 3.2, linux 2.6.18 (centos)

[[email protected] script]# ruby memstats.rb 4946
Process:             4946
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              56,324 kB
  pss                        60,256 kB
  rss                        83,628 kB
  shared_clean                4,204 kB
  shared_dirty               23,100 kB
  size                      108,156 kB
  swap                           68 kB 

Если я полностью завершу мастер-процесс (а не только HUP), перезагрузите его и сразу же проверьте работника до того, как все запросы поставят в очередь, я получаю лучшую историю:

[[email protected] script]# ruby memstats.rb 5743
Process:             5743
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              21,572 kB
  pss                        27,735 kB
  rss                        66,296 kB
  shared_clean                2,484 kB
  shared_dirty               42,240 kB
  size                       91,768 kB
  swap                            0 kB

Но в течение 5 секунд после запуска они вернутся к ~ 20 МБ shared_clean + shared_dirty.

Я подозревал, что замена может вызвать проблему, но после снижения swappiness и обеспечения того, чтобы ни родительский, ни дочерний процессы не были заменены (с использованием swapstats.rb), проблема сохраняется.

Я не понимаю, что такое shared_dirty память, и как она превращается в частную память. Мне также нравятся предложения по улучшению долговечности и объема моей общей памяти. Спасибо!

4b9b3361

Ответ 1

В соответствии с этим ответом, который вы, возможно, уже видели, есть строка, которая гласит:

Обратите внимание, что страница "share-able" считается приватным сопоставлением, пока она фактически разделяется. т.е. если в настоящее время используется только один процесс libfoo, этот раздел текста библиотеки появится в процессе частные сопоставления. Он будет учитываться в общих сопоставлениях (и удаляется из частных) только в том случае, если/при запуске другого процесса используя эту библиотеку.

Что я сделал бы, чтобы проверить, получаете ли вы преимущества описанные в этой статье, помещается файл размером 10 МБ в качестве литерала строка непосредственно в исходный код. Затем, если вы запустите 20 рабочих, вы сможете узнать, используете ли вы 200 МБ памяти или всего 10 МБ, как ожидается, с новой функцией сбора мусора.

UPDATE:

Я просмотрел источник единорога и нашел ссылку на эта замечательная статья.

Подводя итог, в нем говорится, что для того, чтобы адаптировать ваши приложения, чтобы воспользоваться преимуществами коллективного сборщика мусора Ruby Enterprise Edition, вы должны установить GC.copy_on_write_friendly в true, прежде чем вы откроете fork.

if GC.respond_to?(:copy_on_write_friendly=)
    GC.copy_on_write_friendly = true
end

На основе вашего предоставленного конфигурационного файла единорога, похоже, отсутствует назначение.

Кроме того, мне понравилось читать эти связанные статьи:

В соответствии с страницей fork man:

В Linux, fork() реализуется с использованием страниц копирования на запись, поэтому только штраф, который он несет, - это время и память, необходимые для дублировать таблицы родительских страниц и создавать уникальную задачу структура для ребенка.

Начиная с версии 2.3.3, вместо того, чтобы ссылаться на ядро ​​fork() вызов, оболочка glibc fork(), которая предоставляется как часть NPTL реализация threading вызывает clone (2) с флагами, которые обеспечивают тот же эффект, что и традиционный системный вызов. (Вызов fork() - эквивалентно вызову клона (2), определяющему флаги только как SIGCHLD.) Оболочка glibc вызывает любые обработчики вил, которые были установлены используя pthread_atfork (3).

И в соответствии с страницей клонирования:

В отличие от fork (2), эти вызовы позволяют дочернему процессу совместно использовать части его контекст выполнения с вызывающим процессом, таким как память пространство, таблицу файловых дескрипторов и таблицу сигналов обработчики.

Итак, я читаю это, чтобы означать: linux fork copy-on-write, которая является функцией, которую единорог полагается на реализацию совместного использования памяти, не была реализована до libc 2.2.3 (пожалуйста, кто-то меня исправит, если я В этой интерпретации неверно.

Чтобы проверить, какая версия libc вы используете, вы можете ввести:

ldd --version

Или найдите glibc и запустите его напрямую. В моей системе он нашел файл в следующем месте:

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6