在Perl脚本中保留较低内存使用率的一些好建议是什么?我有兴趣学习如何根据Perl程序为系统保持尽可能低的内存占用。我知道Perl在内存使用方面不是很好,但我想知道是否有改进它的提示。
那么,你可以做些什么来保持Perl脚本使用更少的内存。我对任何建议感兴趣,无论它们是编写代码的实际技巧,还是如何以不同方式编译Perl的技巧。
编辑Bounty: 我有一个perl程序,用作网络应用程序的服务器。连接到它的每个客户端当前都会获得它自己的子进程。我也使用过线程而不是forks,但我还是无法确定使用线程而不是forks实际上是否更节省内存。
我想尝试再次使用线程而不是forks。我相信理论上应该节省内存使用量。我在这方面有几个问题:
使用Perl / Linux中的线程,在每个线程的基础上确定实际内存使用量的最可靠方法是什么?
答案 0 :(得分:79)
您遇到了什么问题,“大”对您意味着什么?我有朋友你需要将200 Gb文件加载到内存中,所以他们的好提示的想法与预算购物者有很大不同,因为最小的VM切片遭受250 Mb的RAM(真的吗?我的手机有更多)。
通常,Perl会保留您使用的任何内存,即使它没有使用它。意识到在一个方向上进行优化,例如记忆,可能会对另一个产生负面影响,比如速度。
这不是一个全面的列表(Programming Perl中还有更多):
☹使用Perl内存分析工具来帮助您找到问题区域。请参阅Profiling heap memory usage on perl programs和How to find the amount of physical memory occupied by a hash in Perl?
☹使用可能范围最小的词法变量,以便Perl在您不需要时重新使用该内存。
☹避免创建大型临时结构。例如,读取带有foreach
的文件会立即读取所有输入。如果您只需逐行使用,请使用while
。
foreach ( <FILE> ) { ... } # list context, all at once
while( <FILE> ) { ... } # scalar context, line by line
☹您甚至可能不需要将文件存储在内存中。 Memory-map files instead of slurping them
☹如果您需要创建大数据结构,请考虑DBM::Deep或其他存储引擎之类的内容,以便将大部分内容保留在RAM和磁盘上,直到您需要它为止。
☹不要让人们使用你的程序。每当我这样做,我都会将内存占用减少大约100%。它还减少了支持请求。
by通过引用传递大块文本和大聚合,这样就不会复制,因此会存储两次相同的信息。如果你因为想要改变某些东西而必须复制它,那么你可能会陷入困境。这可以作为子程序参数和子程序返回值的两种方式:
call_some_sub( \$big_text, \@long_array );
sub call_some_sub {
my( $text_ref, $array_ref ) = @_;
...
return \%hash;
}
☹追踪模块中的内存泄漏。在我意识到a module wasn't releasing memory之前,我对应用程序遇到了很大的问题。我在模块的RT队列中找到了一个补丁,应用它并解决了问题。
☹如果您需要处理大量数据但不想持久内存占用,请将工作卸载到子进程。子进程在其工作时仅具有内存占用。当您得到答案时,子进程将关闭并释放内存。同样,工作分配系统,例如Gearman,可以在机器之间传播工作。
☹将递归解决方案转换为迭代解决方案。 Perl没有尾递归优化,因此每个新调用都会添加到调用堆栈中。您可以使用goto或模块的技巧来自行优化尾部问题,但要完成您可能不需要的技术需要做很多工作。
☹他是使用6 Gb还是只使用5 Gb?好吧,说实话,在所有这些兴奋中,我有点迷失自己。但是,因为这是世界上最强大的语言Perl,并且会让你的记忆清醒,你必须问自己一个问题:我是否感到幸运?好吧,你做朋克吗?
还有更多,但是早上太早才弄明白这些是什么。我在Mastering Perl和Effective Perl Programming中介绍了一些内容。
答案 1 :(得分:4)
我的两分钱。
在Perl中创建的线程是否阻止将Perl模块库复制到每个线程的内存中?
线程(使用线程)是在Perl中创建线程的最有效方式(或唯一)吗?
在线程中,我可以指定一个stack_size参数,具体应该考虑什么时候 指定此值,以及它如何影响内存使用?
使用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 :(得分:2)
如果你真的很绝望,你可以尝试将一些内存作为文件系统(tmpfs / ramdisk)并在其上读/写/删除文件。我想tmpfs实现足够聪明,可以在删除文件时释放内存。
您还可以在tmpfs上mmap(请参阅File::Map,Sys::Mmap)一个大文件,这是我从Cache::FastMmap获得的一个想法。
从未尝试过,但它应该有效:)
答案 3 :(得分:2)
除了brian d foy的建议外,我发现以下内容也有很多帮助。
Brian提到使用范围尽可能小的词法变量。如果您正在使用&#34; undef&#34;还有助于立即释放内存以供Perl重用。所以你声明了一个标量,数组,哈希甚至是子,当你完成其中任何一个时,使用:
my(@divs)= localtime(time); $ VAR {分钟} = $ divs [1];
undef @divs; undef @array; undef $标量; undef%hash; undef&amp; sub;
并且不要使用任何不必要的变量来缩小代码。最好的硬编码可以减少名称空间的使用。
然后,根据您的应用程序的功能,您可以尝试许多其他技巧。我们的每一分钟都由cron经营。我们发现我们可以通过睡眠(30)分叉一半的进程,因此一半会在前30秒内运行并完成,释放cpu和内存,另一半将在30秒延迟后运行。再次将资源使用量减半。总而言之,我们设法将RAM使用率从2 GB以下降低到200MB,节省了90%。
我们设法通过
很好地了解内存使用情况top -M
因为我们的脚本是在只有一个站点的相对稳定的服务器上执行的。所以观看&#34;免费公羊&#34;给了我们关于memery用法的很好的指示。
另外&#34; ps&#34; grepping你的脚本,如果分叉,按内存或cpu使用排序是一个很好的帮助。
ps -e -o pid,pcpu,pmem,stime,etime,command --sort=+cpu | grep scriptname | grep -v grep
答案 4 :(得分:1)
线程和分支都将是CoW(写入时复制)内存页面。使用线程,您可以定义共享变量,但默认情况下会复制每个线程的变量。在这两种情况下,您都可以期望更高的内存使用率。
我不确切知道您正在处理哪种应用程序,但您可能需要考虑使用事件驱动模型而不是父/子进程来编写应用程序。我建议你看看AnyEvent这很简单,如果应用程序成为单线程(或进程),你将节省一些内存(在某些情况下更快)。人们甚至用AnyEvent编写的Web服务器具有非常好的性能,你几乎不会注意到它是单线程的。以Twiggy
为例答案 5 :(得分:-7)
尝试使用更多缓存。实现缓存例程的逻辑总是相同的,因此您可以使用CPAN模块Memoize自动执行。使用Devel::Size检查实际内存占用量。