计算机世界没有免费的午餐。特别地,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的内存占用依然几乎与线程数成正比。