因此,一个朋友注意到对numpy感到好奇。这是一个最小的示例,该示例首先顺序运行同一脚本,而不是两个实例在各自的进程中并行运行:
#!/bin/bash
# This is runner.sh
fl=/tmp/$(mktemp test_XXXXX.py)
trap "rm -fv '$fl'" EXIT
cat - > "$fl" <<-'EndOfHereDoc'
#!/usr/bin/env python
import numpy as np
import sys
if __name__ == '__main__':
if len(sys.argv)>1: print(sys.argv[1] +' start: '+ str(datetime.datetime.now()))
cube_size=100
cube=np.zeros((cube_size,cube_size,cube_size))
cube_ones=np.ones((cube_size,cube_size,cube_size))
for x in range(10000):
np.add(cube_ones,cube,out=cube)
if len(sys.argv)>1: print(sys.argv[1] +' start: '+ str(datetime.datetime.now()))
EndOfHereDoc
echo "Serial"
time python "$fl" 0
echo
echo "Parallel"
time python "$fl" 1&
time python3 "$fl" 2&
wait
rm -fv "$fl"
trap '' EXIT
其输出是:
$ runner.sh
Serial
0 start: 2018-09-19 15:46:52.540881
0 end: 2018-09-19 15:47:04.592280
real 0m12,105s
user 0m12,084s
sys 0m0,020s
Parallel
1 start: 2018-09-19 15:47:04.665260
2 start: 2018-09-19 15:47:04.780635
2 end: 2018-09-19 15:47:27.053261
real 0m22,480s
user 0m22,448s
sys 0m0,128s
1 end: 2018-09-19 15:47:27.097312
real 0m22,505s
user 0m22,409s
sys 0m0,040s
removed '/tmp/test_zWN0V.py'
没有加速。好像进程在一个接一个地运行。我假设numpy仅在使用资源,而其他进程等待该资源被释放。 但是这里到底发生了什么? GIL应该只是多线程问题,而不是多个进程,对吗?我觉得特别奇怪,p2不仅仅是在等待p1完成。取而代之的是,两个过程都需要约22秒才能完成。我希望一个人能获得资源并在一半的时间内完成。而另一个则等到第一个释放后,再花费约12秒的时间。
请注意,在multiprocessing
中使用python自己的Pool
模块运行python代码时,也会发生这种情况。但是,如果您执行的操作不涉及某些特定的numpy函数,例如:
cube_size=25
cube=[0 for i in range(cube_size**3)]
for x in range(10000):
cube = [ value + 1 for value in cube]
编辑:
我有一个真正的4核CPU。我谨记超线程,这不是这里的问题。在单个过程部分中,一个CPU处于100%状态,其余处于空闲状态。在两个过程部分中,两个处于100%的状态,其余部分处于空闲状态(根据htop
)。我了解numpy在后台运行ATLAS,LAPACK和BLAS库,它们不是Python(实际上是纯C或Fortran)。这些可以利用并行技术。我的问题是,为什么CPU使用率没有显示出来?
答案 0 :(得分:4)
与核心Python一样,Numpy不受GIL的限制。这是因为numpy仅将数组存储为Python对象。实际的数据本身存储为用C定义的“原始”类型。这也是为什么在numpy数组上进行迭代比在Python列表上进行迭代要慢得多的原因。 numpy数组必须为其产生的每个值构建一个Python对象,而Python列表中已经包含Python对象。
由于numpy不受GIL的限制,因此它可以使用可用的线程数学库。也就是说,并行进程的运行时间更长,因为每个进程已经使您的计算机达到极限,因此两个进程都在争夺相同的资源。
看一下输出,看看机器中有什么可用(警告它很冗长)。
import numpy.distutils.system_info as sysinfo
sysinfo.show_all()