Избегайте выделения памяти только для чтения 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);
Примечание: чтобы это работало, дочерний элемент должен выйти также до того, как структура данных выйдет из области видимости.