Избегайте выделения памяти только для чтения forked() при выходе из Perl

В Perl я генерирую огромную структуру данных только для чтения, а затем fork(),

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

Есть ли способ избежать этого бесполезного распределения?

Вот пример кода Perl, который показывает проблему.

#! /usr/bin/perl
my $a = [];
# Allocate 100 MiB
for my $i (1 .. 100000) {
        push @$a, "x" x 1024;
}
# Fork 10 other process
for my $j (1 .. 10) {
        last unless fork();
}
# Sleep for a while to be able to see the RSS
sleep(5);

В образце vmstat В результате мы видим, что сначала он выделяет только 100 МБ, затем после первого сна он на короткое время выделяет целое, а затем освобождает все.

procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 1329660  80596  86936    0    0    21    18  160   25  0  0 100  0  0
 1  0      0 1328048  80596  86936    0    0     0     0 1013   44  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1028   76 11  5 84  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1010   40  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1026   54  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1006   39  0  0 100  0  0
13  0      0 741156  80596  86936    0    0     0     0 1012   66 13 58 28  0  0
 0  0      0 1329288  80596  86936    0    0     0     0 1032   60  0  0 100  0  0

Примечание: кажется, что это не специфическая для Perl проблема. Как я тестировал 5.8.8, 5.10.1 и 5.14.2, и все они демонстрируют это поведение.

Обновить:

Как спросил @choroba в комментариях, я тоже пытался undef структура данных, но кажется, что она вызывает касание памяти при выделении ОЗУ.

Вы можете добавить следующий фрагмент в конце первого сценария.

# Unallocate $a
undef $a;
# Sleep for a while to be able to see the RSS
sleep(5);

1 ответ

Решение

На самом деле, как я выяснил сам, это поведение является особенностью, и ответ лежит в Perl doc:

exit() Функция не всегда завершается немедленно. Точно так же любые деструкторы объекта, которые должны быть вызваны, вызываются до реального выхода. Если это проблема, вы можете позвонить POSIX::_exit($status) избежать END и деструктор обработки.

И действительно, добавление его в конец исходного кода кода позволяет избежать такого поведения.

# XXX - To be added just before ending the process
# Use POSIX::_exit($status) to end without allocating copy-on-write RAM
use POSIX;
POSIX::_exit(0);

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

Другие вопросы по тегам