【发布时间】:2020-05-03 00:54:30
【问题描述】:
以下代码是图像二值化算法的一部分。我正在尝试提高这个算法的性能,但我发现瓶颈在一个奇怪的地方。 (算法中不相关的部分省略。)
@jit(nopython=True)
def make_buckets(img):
height, width = img.shape
buckets = np.zeros(256)
# buckets = np.full(256, 0) [I tried both, sometimes one works better than other]
for h in range(height):
for w in range(width):
g = img[h, w]
a = buckets[g] + 1
# buckets[g] = 1
# buckets[g] = a
img = np.random.randint(0, 256, (224, 224))
%timeit make_buckets(img)
以上代码的运行时间为:
539 ns ± 12.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
这很好。这意味着,在循环中获取/访问buckets[g] 一点也不苛刻。让我们看看如果我取消注释这一行会发生什么:buckets[g] = 1
for h in range(height):
for w in range(width):
g = img[h, w]
a = buckets[g] + 1
buckets[g] = 1
# buckets[g] = a
运行时是,
27.8 µs ± 865 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这意味着在循环内设置buckets[g] 会大大增加运行时间。
现在,如果我像这样取消注释 buckets[g] = a 行:
for h in range(height):
for w in range(width):
g = img[h, w]
a = buckets[g] + 1
# buckets[g] = 1
buckets[g] = a
运行时间增加到:
37.8 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这个运行时间增量是一致的,并且取决于img 矩阵,它可能需要>100 µs。这也意味着将矩阵元素设置为变量比将其设置为常数要慢得多。有人可以解释为什么在循环中将矩阵元素设置为变量会大大增加运行时间吗?另外,你能建议 fs 有什么方法可以提高性能吗?
我正在使用数百万张图像(手写文档)进行机器学习,所以我的数据生成器应该足够快,这样它就不会成为 GPU 的瓶颈。在数据生成器管道中,有多个图像操作,所以我想尽可能地优化每个操作。这种二值化是在每张图像上完成的,所以我想将运行时间缩短到几微秒。
【问题讨论】:
-
请记住,numpy 数组使用静态数据类型,与 python 列表或变量不同。出于这个原因,从 python 变量设置元素需要安全检查/转换,因此我们预计这比读取已经具有强制类型的元素花费更多时间。但是,我不知道这是否说明了您在此处看到的全部差异,或者是否有其他因素在起作用。
-
如果您所做的只是阅读,则可以优化这些阅读。尝试用你正在阅读的数据实际做一些事情,这些事情不能仅仅被优化掉。
-
@HymnsForDisco,确实,当我尝试使用 python list
buckets = [0 for _ in range(256)]而不是buckets = np.zeros(256)时,(然后将列表转换为 numpy)性能稍微好一些,有时高达 20%。 -
有一个死代码消除。在第一个示例中,您没有做任何影响函数输出的事情 -> 代码被检测为无用并被忽略。