Советы по снижению использования памяти Perl

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

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

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

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

  1. потоки, созданные в Perl, предотвращают копирование библиотек модулей Perl в память для каждой нити?
  2. Is темы (используйте потоки) наиболее эффективный способ (или единственный) способ создания потоков в Perl?
  3. в потоках я могу указать параметр stack_size, что конкретно следует учитывать при указании этого значения, и как это влияет использование памяти?

с потоками в Perl / Linux, каков наиболее надежный метод определения фактического использования памяти на основе каждого потока?

6 ответов


С какой проблемой вы сталкиваетесь, и что означает для вас "большой"? У меня есть друзья, которым нужно загрузить 200 Гб файлов в память, поэтому их идея хороших советов сильно отличается от бюджетного покупателя для минимальных срезов VM, страдающих от 250 Мб ОЗУ (действительно? Мой телефон имеет больше, чем это).

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

это не полный список (и больше в Программирование На Perl):

☹ используйте инструменты профилирования памяти Perl, чтобы найти проблемные области. См.профилирование использования памяти кучи в программах perl и как найти объем физической памяти, занятой хэшем в Perl?

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

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

 foreach ( <FILE> ) { ... } # list context, all at once 
 while( <FILE> ) { ... } # scalar context, line by line

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

☹ Если вам нужно создать структуры больших данных, рассмотрите что-то вроде DBM:: Deep или другие системы хранения, чтобы сохранить большую часть его из оперативной памяти и на диске, пока не понадобится.

☹ не позволяйте людям использовать вашу программу. Всякий раз, когда я это делал, я уменьшал объем памяти примерно на 100%. Он также сокращает запросы на поддержку.

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

 call_some_sub( $big_text, \@long_array );
 sub call_some_sub {
      my( $text_ref, $array_ref ) = @_;
      ...
      return \%hash;
      }

Track отслеживание утечек памяти в модулях. У меня были большие проблемы с приложением, пока я не понял, что модуль не освобождал память. Я нашел патч в очереди RT модуля, применил его и решил проблему.

☹ Если вам нужно обработать большой кусок данных один раз, но не хотите постоянный объем памяти, разгрузите работу в дочерний процесс. Дочерний процесс имеет только объем памяти во время работы. Когда ты ... ответ, дочерний процесс выключается и освобождает его память. Аналогично работают системы распределения, такие как Gearman, может распределить работу среди машин.

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

☹ он использовал 6 Гб или только пять? Ну, сказать по правде, во всем этом волнении я и сам сбился со следа. Но, поскольку это Perl, самый мощный язык в мире, и он полностью уничтожит вашу память, вы должны задать себе один вопрос: чувствую ли я себя счастливым? Ну, да, сопляк?

есть еще много, но слишком рано утром, чтобы понять, что это такое. Я покрываю некоторые в Освоение Perl и Эффективный Perl Программирование.


мои два десятицентовика.

  1. потоки, созданные в Perl, предотвращают копирование библиотек модулей Perl в память для каждого потока?

    • это не так, это всего лишь один процесс, что не повторяется в стеке программы, каждый поток должен иметь собственную.
  2. являются ли потоки (использовать потоки) наиболее эффективным (или единственным) способом создания потоков в Perl?

    • IMO любой метод в конечном итоге вызывает pthread библиотека API, которая фактически выполняет работу.
  3. в потоках я могу указать параметр stack_size, что конкретно следует учитывать, когда указание этого значения и как оно влияет на использование памяти?

    • поскольку потоки выполняются в одном и том же пространстве процессов, стек не может быть общим. Размер стека говорит pthreads как далеко они должны быть друг от друга. Каждый раз, когда вызывается функция локальные переменные размещаются в стеке. Так размер стека ограничивает глубину рекурсии. вы можете выделить как можно меньше, чтобы расширить, что ваше приложение все еще работает.

с потоками в Perl / Linux, каков наиболее надежный метод определения фактического использования памяти на основе на поток?

* Stack storage is fixed after your thread is spawned, heap and static storage is shared and
  they can be used by any thread so this notion of memory usage per-thread doesn't really
  apply. It is per process.


Comparing fork and thread:

* fork duplicate the process and inherites the file handles

  advantages: simpler application logic, more fault tolerant.
              the spawn process can become faulty and leaking resource
              but it will not bring down the parent. good solution if
              you do not fork a lot and the forked process eventually
              exits and cleaned up by the system.

  disadvantages: more overhead per fork, system limitation on the number
              of processes you can fork. You program cannot share variables.

* threads runs in the same process with addtional program stacks.

  advantages: lower memory footprint, thread spawn if faster and ligther
              than fork. You can share variables.

  disadvantages: more complex application logic, serialization of resources etc.
              need to have very reliable code and need to pay attention to
              resource leaks which can bring down the entire application.

IMO, depends on what you do, fork can use way less memory over the life time of the 
application run if whatever you spawn just do the work independently and exit, instead of
risking memory leaks in threads.

Если вы действительно в отчаянии, вы можете попытаться подключить некоторую память в качестве файловой системы (файловая система tmpfs / ramdisk) и чтение/запись/удаление файлов на нем. Я думаю, что реализация tmpfs достаточно умна, чтобы освободить память при удалении файла.

вы также можете mmap (см. File:: Map, Sys:: Mmap) огромный файл на tmpfs, идея, которую я получил от Кэш::FastMmap.

никогда не пробовал, но должно работать :)


оба потока и вилки будут CoW (копировать на запись) страницы памяти. С помощью потоков вы можете определить общие переменные, но по умолчанию будете копировать переменные в поток. В обоих случаях можно ожидать более высокого использования памяти.

Я не знаю точно, с каким приложением вы имеете дело, но вы можете рассмотреть возможность написания своего приложения с помощью модели, управляемой событиями, а не родительских/дочерних процессов. Я бы рекомендовал вам взглянуть на AnyEvent это довольно просто и учитывая, что приложение становится одиночным потоком (или процессом), вы сохраните некоторую память (и даже быстрее в некоторых случаях). Люди даже написали веб-серверы с AnyEvent с очень хорошей производительностью, и вы почти не заметили, что он однопоточный. Посмотрите, например, на Твигги


в дополнение к предложениям Брайана d foy, я обнаружил, что следующее также очень помогло.

  1. где возможно, не" используйте " внешние модули, вы не знаете, сколько памяти они используют. Я нашел, заменив LWP и HTTP::Request::Common модули с использованием памяти Curl или Lynx сокращены наполовину.
  2. разрезал его снова, изменив наши собственные модули и потянув только необходимые подпрограммы, используя "require", а не полную библиотеку ненужных субтитры.
  3. Брайан упоминает использование лексических переменных с наименьшей возможной областью. Если вы раздваиваете, использование "undef" также помогает, немедленно освобождая память для повторного использования Perl. Таким образом, вы объявляете скаляр, массив, хэш или даже sub, и когда вы закончите с любым из них, используйте :

    my (@divs) = местное время(время); $VAR{minute} = $divs[1];

    undef @divs; undef @ array; undef $скаляр; undef %хэш; фдоон ⊂

  4. и не используйте ненужные переменные, чтобы сделать ваш код меньше. Лучше жестко кодировать все, что возможно, чтобы уменьшить использование пространства имен.

затем есть много других уловок, которые вы можете попробовать в зависимости от функциональности вашего приложения. Нашей каждую минуту заправлял крон. Мы обнаружили, что можем развить половину процессов с помощью сна(30), поэтому половина будет работать и завершаться в течение первых 30 секунд, освобождая процессор и память, а другая половина будет работать после 30-секундной задержки. Снова вдвое сократил использование ресурсов. В целом, нам удалось сократить использование ОЗУ с более чем 2 ГБ до 200 МБ, что на 90% экономит.

нам удалось получить довольно хорошее представление о памяти с

top -M

как наш скрипт был выполнен на относительно стабильном сервере только один сайт. Поэтому просмотр "free ram" дал нам довольно хорошее представление об использовании memery.

также" ps " grepping для вашего скрипта, и если разветвление, сортировка по памяти или использованию процессора была хорошей помощь.

ps -e -o pid,pcpu,pmem,stime,etime,command --sort=+cpu | grep scriptname | grep -v grep

попробуйте использовать больше кэширования. Логика реализации процедуры кэширования всегда одинакова, поэтому вы можете автоматизировать использование CPAN module Memoize. Использовать Devel:: Size для проверки фактического объема памяти.