保持Perl内存使用率低的技巧
在Perl脚本中保持内存使用率低的一些好的技巧是什么? 我有兴趣学习如何保持我的内存占用尽可能低的系统取决于Perl程序。 我知道Perl在内存使用方面并不是很好,但是我想知道是否有改进它的提示。
那么,你可以做些什么来保持一个Perl脚本使用较less的内存。 我对任何build议感兴趣,无论是写代码的实际技巧,还是关于如何以不同方式编译Perl的技巧。
编辑赏金:我有一个perl程序,作为一个networking应用程序的服务器。 每个连接到它的客户端当前都有自己的subprocess。 我已经使用线程,而不是叉,但我一直无法确定,如果使用线程而不是福克斯实际上是更高效的内存。
我想尝试使用线程而不是叉子。 我相信在理论上应该节省内存使用。 在这方面我有几个问题:
- Perl中创build的线程是否阻止将Perl模块库复制到每个线程的内存中?
- 线程 (使用线程)是在Perl中创build线程的最有效的方法(或唯一的方法)吗?
- 在线程中,我可以指定一个stack_size参数,在指定这个值的时候应该考虑什么,以及它如何影响内存的使用?
使用Perl / Linux中的线程,在每个线程的基础上确定实际内存使用情况的最可靠方法是什么?
你遇到了什么样的问题,“大”对你意味着什么? 我有朋友,你需要加载200 GB的文件到内存,所以他们的好的提示的想法是很多不同的预算购物者的最小VM片250Mb的内存(真的?我的手机有更多的)痛苦。
一般来说,即使Perl没有使用它,Perl也会保留你使用的任何内存。 意识到一个方向的优化,例如记忆,可能会对另一个方面产生负面影响,如速度。
这不是一个全面的列表(在Perl编程中还有更多):
☹使用Perl内存分析工具来帮助您查找问题区域。 请参阅在perl程序上分析堆内存使用情况以及如何 在Perl 中查找散列占用的物理内存量?
☹尽可能使用范围最小的词法variables,以便Perl在不需要时重新使用该内存。
☹避免创build大的临时结构。 例如,用foreach
读取一个文件立即读取所有的input。 如果你只需要一行一行地使用while
foreach ( <FILE> ) { ... } # list context, all at once while( <FILE> ) { ... } # scalar context, line by line
☹你可能甚至不需要在内存中的文件。 内存映射文件,而不是sl them他们
☹如果您需要创build大数据结构,请考虑DBM :: Deep或其他存储引擎等大部分内容,直到您需要为止。
☹不要让人们使用你的程序。 每当我这样做,我已经减less了大约100%的内存占用。 它也减less了支持请求。
by通过引用传递大量的文本和大量的聚合,所以你不要复制,从而存储相同的信息两次。 如果因为要改变某些内容而必须复制它,则可能会卡住。 这是作为子程序参数和子程序返回值的两种方式:
call_some_sub( \$big_text, \@long_array ); sub call_some_sub { my( $text_ref, $array_ref ) = @_; ... return \%hash; }
跟踪模块中的内存泄漏。 直到我意识到一个模块没有释放内存 ,我在应用程序中遇到了很大的问题。 我在模块的RT队列中find一个补丁,应用它,并解决了这个问题。
☹如果您需要处理一大块数据,但不想占用内存占用量,请将工作转移到subprocess。 subprocess在运行时只有内存占用。 当你得到答案时,subprocessclosures并释放内存。 同样, Gearman等工作分配系统也可以在机器之间进行分配。
re将recursion解决scheme转换为迭代解决scheme。 Perl没有尾recursion优化,所以每个新的调用都添加到调用堆栈中。 您可以使用goto或模块技巧来自己优化尾部问题,但是这需要很多工作来坚持您可能不需要的技术。
☹他用了6GB还是只用了5GB? 那么,说实话,在这种激动中,我有点失去了自己。 但是,因为这是世界上最强大的语言Perl,而且会打消你的记忆,所以你得问自己一个问题:我感到幸运吗? 那么,做你朋克?
还有更多的东西,但现在清楚这些是什么时候太早了。 我掌握了一些掌握Perl和有效的Perl编程 。
我的两个硬币。
-
Perl中创build的线程是否阻止将Perl模块库复制到每个线程的内存中?
- 它不,它只是一个进程,在程序堆栈中不重复的,每个线程都必须有它自己的。
-
线程(使用线程)是在Perl中创build线程的最有效的方法(或唯一的方法)吗?
- 国际海事组织任何方法最终调用pthread库API的实际工作。
-
在线程中,我可以指定一个stack_size参数,在指定这个值的时候应该考虑什么,以及它如何影响内存的使用?
- 由于线程运行在相同的进程空间中,堆栈不能共享。 堆栈大小告诉pthreads应该离彼此有多远。 每次调用一个函数时,都会在堆栈上分配局部variables。 所以堆栈大小限制了你可以recursion的深度。 您可以尽可能less地分配您的应用程序仍然工作的范围。
使用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上的一个大文件,这是一个来自Cache :: FastMmap的想法。
从来没有尝试过,但它应该工作:)
线程和分叉都将CoW(写入时复制)内存页面。 使用线程可以定义共享variables,但默认情况下会复制每个线程的variables。 在这两种情况下,您都可以预期更高的内存使用量
我不知道你正在处理什么样的应用程序,但你可能要考虑使用事件驱动模型而不是父/subprocess来编写你的应用程序。 我build议你看一下AnyEvent,它非常简单,并且应用程序变成了单线程 (或者进程),你可以节省一些内存(在某些情况下甚至更快)。 人们甚至用AnyEvent编写了web服务器,性能非常好,你几乎不会注意到它是单线程的。 以Twiggy为例来看看
除了布莱恩福伊的build议,我发现以下也帮了很多。
- 在可能的情况下,不要“使用”外部模块,你不知道他们使用了多less内存。 我发现通过replaceLWP和HTTP :: Request :: Common模块,使用Curl或Lynx削减了一半的内存使用量。
- 通过修改我们自己的模块,并使用“require”而不是完整的不需要的子库,只需要修改我们自己的模块,就可以再次削减所需的子程序。
-
Brian提到尽可能使用范围最小的词法variables。 如果你分叉,使用“undef”也可以帮助立即释放内存让Perl重新使用。 所以你声明了一个标量,数组,哈希甚至是子数组,当你完成其中的任何一个时,使用:
my(@divs)= localtime(time); $ VAR {分钟} = $ divs [1];
undef @divs; undef @array; undef $标量; undef%hash; undef&sub;
-
不要使用任何不必要的variables来使代码变小。 尽可能硬编码来减less名称空间的使用。
然后还有很多其他的技巧,你可以尝试取决于你的应用程序的function。 我们每分钟都用cron来运行。 我们发现我们可以用一个睡眠(30)来分叉一半的进程,所以一半会在前30秒内运行并完成,释放CPU和内存,另一半在延迟30秒之后运行。 再次减半资源使用。 总而言之,我们设法将内存使用量从2GB以上降低到200MB,节省了90%。
我们设法得到了一个非常好的内存使用的想法
top -M
因为我们的脚本是在只有一个站点的相对稳定的服务器上执行的。 所以看“免费的公羊”给了我们一个很好的适应症。
另外“ps”grepping你的脚本,如果分叉,按内存或cpu使用sorting是一个很好的帮助。
ps -e -o pid,pcpu,pmem,stime,etime,command --sort=+cpu | grep scriptname | grep -v grep
尝试使用更多的caching。 实现caching例程的逻辑总是相同的,所以你可以自动使用CPAN模块Memoize 。 使用Devel :: Size来检查实际的内存占用情况。