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

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

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

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

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

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

  • Создаются ли потоки, созданные в Perl, для копирования библиотек модулей Perl в память для каждого потока?
  • threads (использовать потоки) наиболее эффективным способом (или единственным) способ создания потоков в Perl?
  • В потоках я могу указать параметр stack_size paramater, что конкретно следует ли учитывать при определении этого значения и как это влияет использование памяти?

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

Мои два десятилетия.

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

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

    • IMO Любой метод в конечном итоге вызывает API-библиотеки pthread, которые фактически выполняют эту работу.
  • В потоках я могу указать параметр 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.

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

В дополнение к предложениям brian d foy, я обнаружил, что следующее также помогло LOT.

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

    my (@divs) = localtime (время); $ VAR {минута} = $divs [1];

    undef @divs; undef @array; undef $scalar; undef% hash; undef & sub;

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

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

Нам удалось получить довольно хорошее представление об использовании памяти с помощью

top -M

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

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

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

Ответ 6

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