仅利用单核的多处理
Multiprocessing only utilizing a single core
我正在尝试使用模块 perlin-noise
创建 FBM 纹理,但执行需要很长时间。我实现了 multiprocessing
,却发现该程序仍然 运行 脱离了单核。我试过寻找其他有同样问题的人,但大多数线程都是 7 年以上和/或涉及与不同 OSs.
相关的问题和解决方案
我的OS是Windows8.1,我是四核CPU,我是运行Python3.9.2
程序如下:
from perlin_noise import PerlinNoise
from multiprocessing import Pool
def world(n):
noise1 = PerlinNoise(octaves=3, seed=1)
noise2 = PerlinNoise(octaves=6, seed=1)
noise3 = PerlinNoise(octaves=12, seed=1)
noise4 = PerlinNoise(octaves=24, seed=1)
noise5 = PerlinNoise(octaves=48, seed=1)
world = []
for i in range(n):
row = []
for j in range(n):
noise_val = noise1([i/n, j/n])
noise_val += 0.5 * noise2([i/n, j/n])
noise_val += 0.25 * noise3([i/n, j/n])
noise_val += 0.125 * noise4([i/n, j/n])
noise_val += 0.0625 * noise5([i/n, j/n])
row.append(noise_val)
world.append(row)
def main():
start = time.time()
nums = [128]
p = Pool()
p.map(world, nums)
p.close()
p.join()
end = time.time()
print(end - start)
if __name__ == '__main__':
import time
from distributed import Client
client = Client()
main()
所以,这是怎么回事?我是不是误以为多处理可以处理这些 for 循环?
谢谢!
只用一个进程的原因很简单。您只在 Pool.map
.
中传递了长度为 1 的列表
Pool(n).map(function, iterable)
所做的是,应用 为提供的 iterable
的 每个 元素提供功能(在此案例,list
) 有 n
个工作进程。
因为你在 nums
中只有 128 个,所以它只创建一个任务,因此没有使用其他进程。
正确的用法应该是这样的:
from multiprocessing import pool
def work(val):
return hash(val ** 10000000)
if __name__ == '__main__':
p = pool.Pool(8)
with p:
output = p.map(work, [i for i in range(1, 31)])
print(output)
此示例将有 8 个进程,因此使用 8 个逻辑核心。由于我们给了它 1 到 30 的数字,p.map
将使用 8 个进程将函数 work
应用于每个数字,因此它可以 运行 同时使用多达 8 个函数。
当我们运行时,我们可以看到它的效果。
当然,它使用更多的进程在进程之间进行通信,等等 - 我承认我不知道它的底层细节。
旁注,为了使您的工作更有效率,您应该尽量避免附加。
检查这个简单的示例,添加大量时间并测量它所花费的时间。
>>> import timeit
>>> def appending():
... output = []
... for i in range(1000000):
... output.append(i)
... return output
...
>>> def list_comp():
... return [i for i in range(1000000)]
>>> print(f"{timeit.timeit(appending, number=100):.2}")
8.1
>>> print(f"{timeit.timeit(list_comp, number=100):.2}")
5.2
如您所见,追加比 List comprehension 慢得多 - 但这并不意味着不使用 list.append
- 只是不要过度使用它。
我正在尝试使用模块 perlin-noise
创建 FBM 纹理,但执行需要很长时间。我实现了 multiprocessing
,却发现该程序仍然 运行 脱离了单核。我试过寻找其他有同样问题的人,但大多数线程都是 7 年以上和/或涉及与不同 OSs.
我的OS是Windows8.1,我是四核CPU,我是运行Python3.9.2
程序如下:
from perlin_noise import PerlinNoise
from multiprocessing import Pool
def world(n):
noise1 = PerlinNoise(octaves=3, seed=1)
noise2 = PerlinNoise(octaves=6, seed=1)
noise3 = PerlinNoise(octaves=12, seed=1)
noise4 = PerlinNoise(octaves=24, seed=1)
noise5 = PerlinNoise(octaves=48, seed=1)
world = []
for i in range(n):
row = []
for j in range(n):
noise_val = noise1([i/n, j/n])
noise_val += 0.5 * noise2([i/n, j/n])
noise_val += 0.25 * noise3([i/n, j/n])
noise_val += 0.125 * noise4([i/n, j/n])
noise_val += 0.0625 * noise5([i/n, j/n])
row.append(noise_val)
world.append(row)
def main():
start = time.time()
nums = [128]
p = Pool()
p.map(world, nums)
p.close()
p.join()
end = time.time()
print(end - start)
if __name__ == '__main__':
import time
from distributed import Client
client = Client()
main()
所以,这是怎么回事?我是不是误以为多处理可以处理这些 for 循环?
谢谢!
只用一个进程的原因很简单。您只在 Pool.map
.
Pool(n).map(function, iterable)
所做的是,应用 为提供的 iterable
的 每个 元素提供功能(在此案例,list
) 有 n
个工作进程。
因为你在 nums
中只有 128 个,所以它只创建一个任务,因此没有使用其他进程。
正确的用法应该是这样的:
from multiprocessing import pool
def work(val):
return hash(val ** 10000000)
if __name__ == '__main__':
p = pool.Pool(8)
with p:
output = p.map(work, [i for i in range(1, 31)])
print(output)
此示例将有 8 个进程,因此使用 8 个逻辑核心。由于我们给了它 1 到 30 的数字,p.map
将使用 8 个进程将函数 work
应用于每个数字,因此它可以 运行 同时使用多达 8 个函数。
当我们运行时,我们可以看到它的效果。
当然,它使用更多的进程在进程之间进行通信,等等 - 我承认我不知道它的底层细节。
旁注,为了使您的工作更有效率,您应该尽量避免附加。
检查这个简单的示例,添加大量时间并测量它所花费的时间。
>>> import timeit
>>> def appending():
... output = []
... for i in range(1000000):
... output.append(i)
... return output
...
>>> def list_comp():
... return [i for i in range(1000000)]
>>> print(f"{timeit.timeit(appending, number=100):.2}")
8.1
>>> print(f"{timeit.timeit(list_comp, number=100):.2}")
5.2
如您所见,追加比 List comprehension 慢得多 - 但这并不意味着不使用 list.append
- 只是不要过度使用它。