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

44

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

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

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

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

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

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

  • 5
    Это очень широкий вопрос. Совет может быть менее случайным, если вы можете предоставить некоторые задачи, которые вы пытаетесь выполнить.
  • 1
    Я думаю, что вы должны разбить ваши дополнительные вопросы на новые вопросы Stackoverflow.
Показать ещё 3 комментария
Теги:
memory

6 ответов

72
Лучший ответ

Какую проблему вы испытываете, и что означает "большой" для вас? У меня есть друзья, вам нужно загружать 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.

  • 0
    Правильно ли, что при создании хэша Perl никогда не освободит используемую память? Можете ли вы повторно использовать тот же хеш для чего-то еще, чтобы сэкономить память?
  • 6
    IIRC, память не освобождается обратно из программы, но сама программа будет повторно использовать то, что уже было заявлено. Поэтому, если ваш хеш выходит за пределы области видимости, а затем вы создаете новый хэш, он, вероятно, будет использовать ту же память, что и старый (много предостережений, но достаточно близко). Также IIRC это не специфическая для Perl проблема, а в основном способ, которым операционные системы распределяют память для программ.
Показать ещё 11 комментариев
4

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

  • Создаются ли потоки, созданные в 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.
  • 2
    С точки зрения сетевого демона, логика приложения вообще не меняется. Фактически, «вилки» CPAN созданы как замена для замены именно по этой причине. Кроме того, по крайней мере в Linux, похоже, что разветвление использует меньше памяти, чем потоки. Библиотеки, кажется, не разделяются, и использование памяти при использовании потоков более чем в 2 раза. Таким образом, все это, как говорится, из практического тестирования, большая часть вашего ответа не соответствует моим собственным результатам. Это может быть связано с моей собственной реализацией, а не общими результатами.
  • 0
    для сетевого демона, который просто слушает и порождает независимую задачу при поступлении входящего запроса, преимущество в том, что он может быть очень легким и просто позволять разветвленному потомку загружать любые ресурсы, которые ему нужны, и все, что нарушает потомок, делает не сбить демона. Потоки стоит учитывать только в том случае, если частота входящего соединения очень высока и большое количество текущих процессов в системе нежелательно.
Показать ещё 7 комментариев
2

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

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

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

  • 1
    Есть ли в этом преимущество не использования RAM-диска, а записи файлов на диск? Может быть, немного быстрее, если у вас медленные физические диски?
  • 0
    Это должно быть намного быстрее! Даже если при использовании операций, оптимизированных для дисков в оперативной памяти, много тупых накладных расходов, оперативная память намного быстрее, чем у дисков.
Показать ещё 3 комментария
1

В дополнение к предложениям 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
1

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

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

  • 0
    Это действительно зависит от того, сколько времени я хочу потратить на написание сетевого демона, и на то, какой метод я разверну. Я считаю, что для правильной модели, управляемой событиями, требуется больше времени. Я не знал ни о каких модулях, которые могли бы помочь с этим, поэтому спасибо за ответ.
  • 0
    Потоки Perl наверняка НЕ делают CoW - каждый созданный perl-поток является реальной "физической" копией.
-8

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

  • 5
    Кэширование увеличит вашу память.
  • 1
    @briandfoy Кэширование всегда увеличивает объем памяти? или какие-то конкретные случаи? Я считаю, что предварительные вычислительные операции, такие как создание справочной таблицы и т. Д., Должны снизить стоимость и ускорить работу.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню