Python multiprocessing + scipy:过多的文件系统'stat'和'open'尝试

Ben*_*mer 3 python random multiprocessing scipy strace

我在Python中观察到一些极端奇怪的行为.请考虑以下代码:

from multiprocessing import Process  
import scipy

def test():
    pass

for i in range(1000):
    p1 = Process(target=test)
    p1.start()
    p1.join()
    print i
Run Code Online (Sandbox Code Playgroud)

当我在这上面运行strace -f时,我从循环中得到以下段:

clone(Process 19706 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b23afde1970) = 19706
[pid 19706] set_robust_list(0x2b23afde1980, 0x18) = 0
[pid 18673] wait4(19706, Process 18673 suspended
 <unfinished ...>
[pid 19706] stat("/apps/python/2.7.1/lib/python2.7/multiprocessing/random", 0x7fff041fc150) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.so", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/randommodule.so", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.py", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.pyc", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/dev/urandom", O_RDONLY) = 3
[pid 19706] read(3, "\3\204g\362\260\324:]\337F0n\n\377\317\343", 16) = 16
[pid 19706] close(3)                    = 0
[pid 19706] open("/dev/null", O_RDONLY) = 3
[pid 19706] fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
[pid 19706] exit_group(0)               = ?
Process 18673 resumed
Process 19706 detached
Run Code Online (Sandbox Code Playgroud)

关于在文件系统中搜索"随机"的所有垃圾怎么办?我真的想避免这种情况,因为我在群集上并行运行了很多具有此结构的进程,并且循环速度非常快,而且这种文件系统活动会阻塞文件系统元数据服务器,或者集群管理员告诉我.

如果我删除"import scipy"命令,那么这个问题就会消失:

clone(Process 23081 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b42ec15e970) = 23081
[pid 23081] set_robust_list(0x2b42ec15e980, 0x18) = 0
[pid 22052] wait4(23081, Process 22052 suspended
 <unfinished ...>
[pid 23081] open("/dev/null", O_RDONLY) = 3
[pid 23081] fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
[pid 23081] exit_group(0)               = ?
Process 22052 resumed
Process 23081 detached
Run Code Online (Sandbox Code Playgroud)

但我需要scipy在我的真实代码中,所以我不能摆脱它.或许我可以,但那将是一种痛苦.

有谁知道为什么我看到这种行为?如果它是某些版本的东西的怪癖,我运行以下:

python:2.7.1,多处理:0.70a1,scipy:0.9.0,

实际上,因为我刚刚意识到它可能是系统依赖的,我在笔记本电脑上运行相同的代码并没有问题(即输出相当于第二种情况).在我正在运行的笔记本电脑上

python:2.6.5,多处理:0.70a1,scipy:0.10.0,

也许这是早期版本的scipy中的一个问题或错误已被修复?我对这类事情的搜索没有任何结果.即使它是问题,在集群上更改scipy的版本也不是那么容易,尽管我可以让集群管理员在需要时构建更新的版本.

这可能是问题吗?

Bra*_*des 5

这不是因为Windows或__main__模块.这也不是Python喜欢做生意的方式.并且,如果你要重新检查,我认为你会发现它是Python 2.6的行为,而不是2.7,除非你运行的是修改后的2.7.

你完全正确的问题源于模块中的随机模块初始化步骤multiprocessing.forking- 它旨在防止你的过程,当它分叉生成n个工人时,创建工人,所有工人都向前迈进完全相同的伪系列随机数(例如,如果他们都使用这些数字协商SSL连接,可能会危及安全性):

        if 'random' in sys.modules:
            import random
            random.seed()
Run Code Online (Sandbox Code Playgroud)

但这里的关键是要意识到从系统调用的角度来看上面的import语句应该是一个无操作,因为如果模块名称已经作为sys.modules字典中的键存在,那么import只需返回它在那里找到的值不试图从文件系统加载任何东西:

>>> import sys
>>> sys.modules['fake'] = 'Not even a module'
>>> import fake
>>> fake
'Not even a module'
Run Code Online (Sandbox Code Playgroud)

if因此,上面引用的声明特别试图在模块甚至没有被加载的情况下防止额外费用.当您在没有加载的情况下进行实验时,语句体甚至都不会触发.importrandomscipyif

那么问题是什么?

问题是2.7之前的旧版本的Python让你在一个包含在一个包中的模块中说两个不同的东西import foo:你可能正在尝试相对导入the_package.foo,或者你可能正在尝试导入顶层包foo.有关为何在最近的Python版本中更改了这种模糊且昂贵的行为的详细信息,请参阅PEP 328:

http://legacy.python.org/dev/peps/pep-0328/

在这种背景下,您可以查看strace输出,注意,没有人在这里解答提到的东西:stat()open()上市系统调用不是要导入模块random,但命名为子虚乌有模块multiprocessing.random!

这是进行额外导入的关键原因,即使random已经列入sys.modules- 因为在允许Python 2.6回到假设该import语句真正意图导入之前random,它必须消除它的可能性尝试相对导入,multiprocessing.random因为该import语句出现在multiprocessing.forking子模块的代码中.

程序员应该说,sys.modules['random'].seed()而不是尝试新的导入,以免你额外的系统调用.但是,一旦你有机会升级到更新版本的Python,希望你不会对这种行为感到困扰.