Python 具有多处理功能的特定标准输出行订单
我有以下问题:Python 具有多处理功能的特定标准输出行订单,python,python-3.x,python-multithreading,Python,Python 3.x,Python Multithreading,我有以下问题: g数据生成器,每一个都是独立的过程,生成n值,我需要以循环(overg)的方式写出该值。因此,对于发电机A,B,C,输出的顺序必须如下所示: <value 1 of A> <value 1 of B> <value 1 of C> <value 2 of A> <value 2 of B> <value 2 of C> <value 3 of A> ... 在执行时,它占用了我所有的4个CPU内
g
数据生成器,每一个都是独立的过程
,生成n
值,我需要以循环(overg
)的方式写出该值。因此,对于发电机A
,B
,C
,输出的顺序必须如下所示:
<value 1 of A>
<value 1 of B>
<value 1 of C>
<value 2 of A>
<value 2 of B>
<value 2 of C>
<value 3 of A>
...
在执行时,它占用了我所有的4个CPU内核,但在性能方面,它比顺序执行慢:
总值10000000的单线程执行时间:
$ time python3 threading_output.py --threads 1 --count 10000000 | wc -l
10000000
real 0m16.557s
user 0m16.443s
sys 0m0.437s
…对于多处理
实现也是如此:
$ time python3 threading_output.py --threads 4 --count 10000000 | wc -l
10000000
real 1m6.446s
user 3m10.073s
sys 0m54.274s
如果不使用mp.Queue
并在product
循环中直接打印生成的值,我将获得约9.6秒的时间,但当然,输出行的顺序不确定
我怎样才能加快速度
更新#1
使用mp.Array
不是共享缓冲区的选项,因为我需要对字符串数组使用ctypec\u wchar\u p
,而根据上下文,这是完全行不通的
更新#2
将mp.Queue(1000)
替换为mp.Pipe(False)
,这将1000万个值的时间缩短到45秒左右。生产者进程现在不再占用CPU,消费者是明显的瓶颈:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5943 ancoron 20 0 28420 15168 8348 R 99.9 0.0 0:12.23 `- python3 threading_output.py --threads 4 --count 10000000
5947 ancoron 20 0 28284 10336 3536 R 29.9 0.0 0:03.69 `- python3 threading_output.py --threads 4 --count 10000000
5948 ancoron 20 0 28284 10336 3536 R 30.8 0.0 0:03.71 `- python3 threading_output.py --threads 4 --count 10000000
5949 ancoron 20 0 28284 10336 3536 R 30.8 0.0 0:03.71 `- python3 threading_output.py --threads 4 --count 10000000
5950 ancoron 20 0 28284 10340 3536 R 29.0 0.0 0:03.58 `- python3 threading_output.py --threads 4 --count 10000000
更新#3
我尝试了,使用了一个简单的
字节队列
,将其缩短到23秒。仍然比单线程慢。好的,所以我做了一些测试,现在我很困惑。我既做了多线程解决方案,也做了异步解决方案,但都没有做得特别好。我还复制并粘贴了你的代码,它总是挂起,即使“完成”了
需要注意的是,在我的代码中,我使用了作为TID的数字,而不是4个随机的十六进制数字,因为我想确保它做的是你想要的。用另一种方法很难分辨,而且很容易改成十六进制
单螺纹:
import random
import sys
def generate():
return random.randrange(-10, 10)
if len(sys.argv) < 2:
print("NEED ARGS")
exit(0)
num = int(sys.argv[1])
for x in range(num):
for _ in range(x):
print("[{}]: {}".format(x, generate()))
随机导入
导入系统
def generate():
返回random.randrange(-10,10)
如果len(系统argv)<2:
打印(“需要参数”)
出口(0)
num=int(sys.argv[1])
对于范围内的x(num):
对于范围(x)内的uu:
打印(“[{}]:{}”。格式(x,generate())
多线程:
from concurrent.futures import TimeoutError
from pebble import ThreadPool, ProcessExpired
import random
import multiprocessing as mp
import sys
def generate():
return random.randrange(-10, 10)
def produce(num):
#tid = '%04x' % random.getrandbits(16)
tid = num
i = 0
while i < num:
print('[%s] %3d' % (tid, generate()))
i += 1
if __name__ == "__main__":
if len(sys.argv) < 2:
print("NEED ARGS")
exit(0)
num = int(sys.argv[1])
with ThreadPool(max_workers=mp.cpu_count()) as pool:
future = pool.map(produce, range(num), timeout=3)
iterator = future.result()
while True:
try:
result = next(iterator)
except StopIteration:
break
except TimeoutError as error:
print(error)
break
except ProcessExpired as error:
print(error)
break
except Exception as error:
print(error)
break
来自concurrent.futures导入TimeoutError
从pebble导入线程池,ProcessExpired
随机输入
将多处理作为mp导入
导入系统
def generate():
返回random.randrange(-10,10)
def产品(数量):
#tid='%04x'%random.getrandbits(16)
tid=num
i=0
而我
老实说,我没看到速度有什么大的变化。多处理的一个实际上是较慢的,这是基本的,因为它可以得到。我刚刚记得的是,它以计算速度著称。我真的不想设置它,但考虑到你的问题的简单、重复和纯粹的计算性质,我认为它可能是你的解决方案
基准是:
.3秒,重复100次
对于1000次迭代,单次为10秒,多次为11秒
我放弃了,因为它花了我很长时间。我不知道如何描述它,但每增加一个数量级,你所做的工作就会增加100倍。使用高斯模式的证明:
你要做的是把每个数的和加到num,这意味着1+2+。。。高斯的模式涵盖了这一点。这应该可以让我们大致了解它有多大:
10作为输入需要550次迭代
100作为输入,需要5050次迭代
1000作为输入需要500500次迭代
10000次输入需要50005000次迭代
通过excel输入数据后,结果是O(n^2),我想这还不错。如果你好奇的话,方程是~.55x^2
您是否介意将您制作的其他程序变体链接起来,以便我可以将它们与我自己的程序进行基准测试?因为老实说,我很想看看他们是否工作正常/我是否做错了什么
Tl;DR:为了便于比较,您使用了哪些测试/代码?你试过派比吗?与打印数字相比,数据库还可以吗(几乎可以肯定会更快)?您是如何让程序单线程运行得如此之快的?
希望这有帮助
编辑:只是为了检查,您确实想执行以下操作,对吗?在第一次迭代中,您打印一次ID和一个随机数。在第二次迭代中,将ID和随机数打印两次。我只是想看看
编辑2:代码应该是固定的
from concurrent.futures import TimeoutError
from pebble import ThreadPool, ProcessExpired
import random
import multiprocessing as mp
import sys
def generate():
return random.randrange(-10, 10)
def produce(num):
tid = '%04x' % random.getrandbits(16)
for _ in range(num):
print('[%s] %3d' % (tid, generate()))
if __name__ == "__main__":
if len(sys.argv) < 3:
print("NEED ARGS")
exit(0)
num = int(sys.argv[1])
workers = int(sys.argv[2])
num_per_worker = int(num/workers)
#The magic numbers here are just optimizations. Feel free to change them
with ThreadPool(max_workers=workers, max_tasks=50) as pool:
future = pool.map(produce, (num_per_worker for _ in range(workers)),
chunksize=round(num/1024))
iterator = future.result()
while True:
try:
result = next(iterator)
except StopIteration:
break
来自concurrent.futures导入TimeoutError
从pebble导入线程池,ProcessExpired
随机输入
将多处理作为mp导入
导入系统
def generate():
返回random.randrange(-10,10)
def产品(数量):
tid='%04x'%random.getrandbits(16)
对于范围内的u(num):
打印(“[%s]%3d%”(tid,generate())
如果名称=“\uuuuu main\uuuuuuuu”:
如果len(sys.argv)<3:
打印(“需要参数”)
出口(0)
num=int(sys.argv[1])
workers=int(sys.argv[2])
num_per_worker=int(num/workers)
#这里的神奇数字只是优化。请随意更改它们
线程池(最大工作线程数=工作线程数,最大任务数=50)作为线程池:
from pebble import ThreadPool, ProcessExpired
import random
import multiprocessing as mp
import sys
from functools import partial
def generate():
return random.randrange(-10, 10)
def produce(num, magic_array):
tid = '%04x' % random.getrandbits(16)
for _ in range(num):
magic_array.append('[%s] %3d' % (tid, generate()))
if __name__ == "__main__":
if len(sys.argv) < 3:
print("NEED ARGS")
exit(0)
num = int(sys.argv[1])
workers = int(sys.argv[2])
num_per_worker = int(num/workers)
magic_array = []
#This is the how the magic array is used as an argument.
#There's probably a better way to do it, but I don't know it
part_produce = partial(produce, magic_array=magic_array)
#The magic numbers here are just optimizations. Feel free to change them
with ThreadPool(max_workers=workers, max_tasks=50) as pool:
future = pool.map(part_produce, (num_per_worker for _ in range(workers)), chunksize=num_per_worker)
iterator = future.result()
while True:
try:
result = next(iterator)
except StopIteration:
break
#This is the important part. For every iteration/worker unit, it will go
#through the list in steps of iteration/worker units, with a start offset
#of x
#Just printing takes about 5 seconds, but I don't think there's a faster
#method because printing takes a long time anyway
for x in range(num_per_worker):
for y in magic_array[x::num_per_worker]:
print(y)
$ time python3 threading_output.py --threads 1 --count 10000000 | wc -l
10000000
real 0m15.915s
user 0m16.045s
sys 0m0.629s
$ time python3 threading_output.py --threads 4 --count 10000000 | wc -l
10000000
real 0m30.005s
user 0m53.543s
sys 0m28.072s
$ time python3 threading_output.py --threads 4 --count 10000000 --output-file test.txt
real 0m6.637s
user 0m18.688s
sys 0m1.265s