计算机世界没有免费的午餐。特别地,CPU时间与内存消耗通常存在此消彼长的关系。实现某个功能,某算法可能使用更多CPU时间但占用更少内存,而另一算法可能使用更少CPU时间而占用更多内存。
而另一种状况是,对于某种功能,某种实现占用更少内存但无法充分利用CPU时间,而另一种实现会消耗更多的内存但能更充分地利用CPU。在线程机制上,Python就属于前者,而Perl属于后者。下面通过几个简单的例子来体验一下这种差别。
CPython 的线程机制使用了 Global Interpreter Lock 来实现,粗略的理解就是由一个解释器程序轮流来执行各个线程。
Perl 的线程机制称为 ithread,粗略理解就是每个线程都有一个独立的解释器,也即是Perl进程的内存空间可能有多个解释器副本。
测试环境
CPU: AMD Phenom(tm) II X6 1055T Processor (六核 2.8GHz)
(也即是每个核心占总CPU时间约 17%)
RAM: 8G
OS: Fedora 15 beta (Linux 2.6.38.2-9.fc15.x86_64)
Python 3.2
Perl 5.12.3
Python 代码:
#! /usr/bin/env python3 import sys import time import threading def main(): if len(sys.argv) == 1: print("%s [-r] num"%sys.argv[0]) exit(1); elif len(sys.argv) == 2: is_sleep = False thr_count = int(sys.argv[1]) else: is_sleep = True thr_count = int(sys.argv[2]) if thr_count < 1: thr_count = 1 if is_sleep: func = dead_loop_with_sleep else: func = dead_loop if thr_count > 1: for i in range(thr_count-1): t = threading.Thread(target=func) t.start() func() def dead_loop(): while True: pass def dead_loop_with_sleep(): while True: time.sleep(1); if __name__ == "__main__": main()
Perl 代码:
#!/usr/bin/env perl use strict; use warnings; use threads; my $is_sleep; my $thr_count; if (!@ARGV){ print("$0 [-r] num\n"); exit(1); } elsif (@ARGV == 1){ $thr_count = int($ARGV[0]); } else { $is_sleep = 1; $thr_count = int($ARGV[1]); } $thr_count = 1 if $thr_count < 1; my $func_ref = $is_sleep ? \&dead_loop_with_sleep : \&dead_loop; if ($thr_count > 1){ for (1..($thr_count - 1)){ threads->create($func_ref); } } &$func_ref(); sub dead_loop{ while(1){} } sub dead_loop_with_sleep{ while(1){ sleep(1); } }
测试结果(内存按Pss计算):
测试形态 | 命令行 | 1 core (pl) | 1 core (py) | all (pl) | all (py) | Mem (pl) | Mem (py) |
---|---|---|---|---|---|---|---|
单线程死循环 | dead_loop 1 | 100% | 100% | 17% | 17% | 0.7M | 8.0M |
6线程死循环 | dead_loop 6 | 近100% | 不可测 | 近100% | 17% | 3.7M | 10.2M |
500线程带睡眠死循环 | dead_loop -r 500 | 0% | 0% | 0% | 0% | 234.1M | 29.8M |
总结
6线程死循环测试表明:Python在切换线程过程中有机会让操作系统将其安排到另一核心上运行,这样各核心的占用率大致平均。也即是,Python解释器在任何时候都只占用一个核心,因此Python占用的总CPU时间比总是 17%。Perl进程则占用了所有CPU核心的几乎所有时间,充分利用了CPU的多核心,但内存占用也几乎与线程数成正比。
当500线程并行时,Python的内存占用增长率显然比线程数增加率要低得多,而Perl的内存占用依然几乎与线程数成正比。