【问题标题】:What is a good approach for a fast FIFO queue?快速 FIFO 队列的好方法是什么?
【发布时间】:2020-06-10 20:48:04
【问题描述】:

我从 python 中的外部设备采样并将值存储在 FIFO 队列中。我有一个固定大小的数组,我从一端将一个新样本排入队列,然后从另一端将“最旧”值出列(我有来自这里的术语:https://stackabuse.com/stacks-and-queues-in-python/)。我为此尝试了不同的实现,每个实现的性能很大程度上取决于 FIFO 数组的大小,请参见下面的示例。是否存在比我收集的更快的方法来执行 FIFO 队列。此外,在这些方法中,除了我可以测量给定大小队列的速度之外,我还应该关注其他问题吗?

import numpy as np
import time
import numba

@numba.njit
def fifo(sig_arr, n):
    for i in range(n):
        sig_arr[:-1] = sig_arr[1:]
        sig_arr[-1] = i
    return

n = 1000000 # number of enqueues/dequeues
for m in [100, 1000, 10000]: # fifo queue length
    print("FIFO array length is:" + str(m))
    print("Numpy-based queue")
    sig_arr_np = np.zeros(m)
    for _ in range(5):
        tic = time.time()
        for i in range(n):
            sig_arr_np[:-1] = sig_arr_np[1:]
            sig_arr_np[-1] = i
        print(time.time() - tic)

    print("Jitted numpy-based queue")
    sig_arr_jit = np.zeros(m)
    for _ in range(5):
        tic = time.time()
        fifo(sig_arr_jit, n)
        print(time.time()-tic)

    print("list-based queue")
    sig_arr_list = [0]*m
    for _ in range(5):
        tic = time.time()
        for i in range(n):
            sig_arr_list.append(i)
            sig_arr_list.pop(0)
        print(time.time() - tic)
print("done...")

输出:

FIFO array length is:100
Numpy-based queue
0.7159860134124756
0.7160656452178955
0.7072808742523193
0.6405529975891113
0.6402220726013184
Jitted numpy-based queue
0.34624767303466797
0.10235905647277832
0.09779787063598633
0.10352706909179688
0.1059865951538086
list-based queue
0.19921231269836426
0.18682050704956055
0.178941011428833
0.190687894821167
0.18914198875427246
FIFO array length is:1000
Numpy-based queue
0.7035880088806152
0.7174069881439209
0.7061927318572998
0.7100749015808105
0.7161743640899658
Jitted numpy-based queue
0.4495429992675781
0.4449293613433838
0.4404451847076416
0.4400477409362793
0.43927478790283203
list-based queue
0.2652933597564697
0.26186203956604004
0.2784764766693115
0.27001261711120605
0.2699151039123535
FIFO array length is:10000
Numpy-based queue
2.0453989505767822
1.9288575649261475
1.9308562278747559
1.9575252532958984
2.048408269882202
Jitted numpy-based queue
5.075503349304199
5.083268404006958
5.181215286254883
5.115811109542847
5.163492918014526
list-based queue
1.2474076747894287
1.2347135543823242
1.2435767650604248
1.2809157371520996
1.237732172012329
done...

编辑:在这里我添加了 Jeff H. 建议的解决方案,并将双端队列设置为固定大小,这样就不需要 .pop() 方法,这样会更快一些。

n = 1000000 # number of enqueues/dequeues
for m in [100, 1000, 10000]: # fifo queue length
    print("deque-list-based queue")
    d = deque([None], m) 
    for _ in range(3):
        tic = time.time()
        for i in range(n):
            d.append(i)
        print(time.time() - tic) 

【问题讨论】:

  • 您是否对脚本进行了概要分析以查看其大部分时间都花在了哪里?如果没有,我建议您在尝试优化它之前这样做。见How can you profile a Python script?
  • 嗨@mar​​tineau。谢谢你的建议。不,我没有这样做。但是我只是比较每个实现中只有两行代码的执行时间。
  • 我正在考虑分析 FIFO 的实现。

标签: python performance fifo


【解决方案1】:

你为什么不尝试自然选择,collections.deque

您上面的所有实现都遭受同样糟糕的性能,因为每次您对任何内容进行入队/出队时,它们都是 O(N) 操作,因为它们都是列表支持的。对于 FIFO,一个合适的数据结构在 O(1) 的恒定时间内完成此操作。

考虑:

从集合导入双端队列

from collections import deque

n = 1000000 # number of enqueues/dequeues
for m in [100, 1000, 10000, 1_000_000]: # fifo queue length
    print(f'\nqueue length: {m}')
    print('deque')
    d = deque(range(m))
    for _ in range(5):
        tic = time.time()
        for i in range(n):
            d.append(i)
            d.pop()
        print(time.time() - tic)
print("done...")

产量:(注意较大的 m 值和接近恒定的时间,在任何尺寸下都优于上述所有值)

queue length: 100
deque
0.13888287544250488
0.13873004913330078
0.13820695877075195
0.1369168758392334
0.1436598300933838

queue length: 1000
deque
0.1434800624847412
0.13672494888305664
0.1380469799041748
0.14961719512939453
0.13932228088378906

queue length: 10000
deque
0.14437294006347656
0.14214491844177246
0.13336801528930664
0.14667487144470215
0.1375408172607422

queue length: 1000000
deque
0.13426589965820312
0.13596534729003906
0.13602590560913086
0.13472890853881836
0.134993314743042
done...
[Finished in 3.4s]

【讨论】:

  • 感谢 Jeff H 的建议。正如您所指出的,对于较大的阵列,它的性能要好得多。当我运行它时,“numpy-solution”的 jitted 版本对于长度低于 150 的数组稍快,但 jitted 版本在长度超过 2000 时非常差,甚至比非 jitted 版本慢。跨度>
  • 我刚刚意识到小数组的 jitted 版本的速度是因为我也 jit 了我猜一般不会工作的 for 循环。所以当我不这样做时,双端队列方法总是最好的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-02
  • 2016-09-29
  • 2012-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多