4
18
2011

Perl 与 Python 3 线程机制实测比较

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

Category: 未分类 | Tags:
2
24
2011

用 Shipwright 构建 Dancer 环境

Shipwright 为在虚拟主机上构建 Perl 应用带来了福音。

Shipwright 是一个软件构建和捆绑环境,用于创建自含的(self-contained)的软件环境。CPAN为我们带来了大量便利高效的模块,但也因此 CPAN 模块通常都有一些依赖关系,更不要说像 Catalyst 那样的庞然大物。Shipwright就是让我们将自己想要的模块以及它的所有依赖包捆绑到一个独立的环境,并且可以方便升级已有的环境或继续添加新包。当然,Shipwright 可以构建多样的软件环境,并不局限于Perl/CPAN。

下面以构建 Dancer 环境作为 Shipwright 的一个最简使用例子。我的系统环境是 Fedora 14。

cpan Shipwright
shipwright create -r fs:/tmp/daner-shipyard
shipwright import -r fs:/tmp/daner-shipyard/ cpan:Dancer
cd /tmp/daner-shipyard/
./bin/shipwright-builder --install-base /tmp/dancer-vessel --skip-test
cd /tmp/dancer-vessel

第1行:安装 Shipwright (希望近期能将 Shipwright 推到 Fedora 主库)

第2行:构建一个 shipyard (造船厂)目录,用于存在源码

第3行:载入 Dancer 源码,自动拉取完整依赖(不包含你正在使用的 Perl 的标准包)

第4行:进入 shipyard 目录

第5行:构建 Dancer 及其完整依赖,安装到 vessel (大船)目录

第6行:进入 vessel 目录,这个目录也就存放着完整的 Dancer 环境

下面来测试一下 Dancer。

如果是用 rpm/deb 的发行版,要先确定是否安装了 CGI 模块,在 Fedora 中可以这样安装:

su -c "yum install 'perl(CGI)'"

然后在当前目录 /tmp/dancer-vessel 创建一个测试小脚本 mydaner.pl:

#!/usr/bin/perl
use lib 'lib/perl5';
use Dancer;

get '/' => sub {
    return 'Hello World!';
};

start;

关键是第2行,将 vessel 的目录加入到 @INC 中。

运行脚本:

perl mydancer.pl

点击: http://0.0.0.0:3000/

Hello World!

 

Category: 未分类 | Tags:
4
9
2010

用C编写Python扩展的简单例子

此例子其实也就是官方的教程中的例子,详见 http://docs.python.org/extending/extending.html

但官方例子中把一段代码拆成若干个段落来解释,我自己看的时候都有点迷惑。在这里我把完整的代码贴出,并加上最简单的步骤。

 

第一步:新建一个文件夹,在里面建一个文件 spammodule.c :

 

#include <Python.h>

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}

static PyObject *SpamError;

static PyMethodDef SpamMethods[] = {
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC
initspam(void)
{
    PyObject *m;

    m = Py_InitModule("spam", SpamMethods);
    if (m == NULL)
        return;

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_INCREF(SpamError);
    PyModule_AddObject(m, "error", SpamError);
}

第二步:建立 setup.py

 

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from distutils.core import setup, Extension

module1 = Extension('spam',
                    sources = ['spammodule.c'])

setup (name = 'spam',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])

第三步:运行命令

 

python setup.py build

运行后的文件夹结构如下:

.
├── build
│   ├── lib.linux-i686-2.6
│   │   └── spam.so
│   └── temp.linux-i686-2.6
│       └── spammodule.o
├── setup.py
└── spammodule.c

测试步:

 

cd build/lib.linux-i686-2.6/
python -c "import spam;spam.system('ls -l')"
ls -l

后面两行命令应该显示相同的结果。

 

 

 

 

 

 

 

 

Category: 未分类 | Tags: python c
3
28
2010

给Thunderbird添加托盘图标

有若干个扩展声称能实现这一功能,包括FireTray, MinimizeToTray Plus, xultray for gecko 1.9.x。

其中xultray for gecko 1.9.x实现的功能最符合习惯,在此推荐一下。

Category: 未分类 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com