gpt4 book ai didi

python - 效率与可读性 : obfuscation when using nested boolean index arrays

转载 作者:太空宇宙 更新时间:2023-11-04 03:41:04 24 4
gpt4 key购买 nike

我正在进行一些非常丑陋的索引。例如,像这样的东西

valid[ data[ index[valid[:,0],0] ] == 0, 1] = False

其中 validindex 分别是 {Nx2} 数组或 boolint,并且 data 是 {N} 长。

如果我非常努力地集中注意力,我可以说服自己这是在做我想做的事……但它令人难以置信地混淆。 我怎样才能有效地取消混淆这样的事情?

我可以将其分解,例如:

valid_index = index[valid[:,0],0]
invalid_index = (data[ valid_index ] == 0)
valid[ invalid_index, 1 ] = False

但是我的数组将有多达 100 个数百万个条目,所以我不想复制内存;我需要尽可能保持速度高效。

最佳答案

这两个代码序列几乎相同,应该具有非常相似的性能。这是我的“直觉”——但后来我进行了静态分析并运行了部分基准测试来确认。

clearer 选项需要多四个字节码来实现,因此可能会稍微慢一些。但是额外的工作仅限于 LOAD_FASTSTORE_FAST,它们只是从堆栈顶部 (TOS) 到/从变量移动。由于额外的工作量不大,因此对性能的影响也应该很小。

您可以在目标设备上对这两种方法进行基准测试以获得更高的定量精度,但在我 3 岁的笔记本电脑上,1 亿个额外的 LOAD_FAST/STORE_FAST 对需要在标准 CPython 2.7.5 上只需 3 秒多一点。所以我估计这种清晰度每 100M 条目将花费你大约 6 秒。而 PyPy即时 Python 编译器不使用相同的字节码,我将清晰版本的开销计时为大约一半,即每 100M 3 秒。与您为处理项目所做的其他工作相比,更清晰的版本可能不是一个重要的摊牌。

TL;DR 背景故事

我的第一印象是代码序列虽然在可读性和清晰度方面有所不同,但在技术上非常相似,并且不应该具有相似的性能特征。但是让我们使用 Python 反汇编器进一步分析一下。我将每个代码片段放到一个函数中:

def one(valid, data):
valid[ data[ index[valid[:,0],0] ] == 0, 1] = False

def two(valid, data):
valid_index = index[valid[:,0],0]
invalid_index = (data[ valid_index ] == 0)
valid[ invalid_index, 1 ] = False

然后使用 Python's bytecode dissassember :

import dis
dis.dis(one)
print "---"
dis.dis(two)

给予:

15           0 LOAD_GLOBAL              0 (False)
3 LOAD_FAST 0 (valid)
6 LOAD_FAST 1 (data)
9 LOAD_GLOBAL 1 (index)
12 LOAD_FAST 0 (valid)
15 LOAD_CONST 0 (None)
18 LOAD_CONST 0 (None)
21 BUILD_SLICE 2
24 LOAD_CONST 1 (0)
27 BUILD_TUPLE 2
30 BINARY_SUBSCR
31 LOAD_CONST 1 (0)
34 BUILD_TUPLE 2
37 BINARY_SUBSCR
38 BINARY_SUBSCR
39 LOAD_CONST 1 (0)
42 COMPARE_OP 2 (==)
45 LOAD_CONST 2 (1)
48 BUILD_TUPLE 2
51 STORE_SUBSCR
52 LOAD_CONST 0 (None)
55 RETURN_VALUE

18           0 LOAD_GLOBAL              0 (index)
3 LOAD_FAST 0 (valid)
6 LOAD_CONST 0 (None)
9 LOAD_CONST 0 (None)
12 BUILD_SLICE 2
15 LOAD_CONST 1 (0)
18 BUILD_TUPLE 2
21 BINARY_SUBSCR
22 LOAD_CONST 1 (0)
25 BUILD_TUPLE 2
28 BINARY_SUBSCR
29 STORE_FAST 2 (valid_index)

19 32 LOAD_FAST 1 (data)
35 LOAD_FAST 2 (valid_index)
38 BINARY_SUBSCR
39 LOAD_CONST 1 (0)
42 COMPARE_OP 2 (==)
45 STORE_FAST 3 (invalid_index)

20 48 LOAD_GLOBAL 1 (False)
51 LOAD_FAST 0 (valid)
54 LOAD_FAST 3 (invalid_index)
57 LOAD_CONST 2 (1)
60 BUILD_TUPLE 2
63 STORE_SUBSCR
64 LOAD_CONST 0 (None)
67 RETURN_VALUE

相似但不相同,而且顺序不一样。快速diff of the two显示相同,加上更清晰的功能需要更多字节代码的可能性。

我从每个函数的反汇编程序列表中解析出字节码操作码,将它们放入 collections.Counter 中,并比较计数:

Bytecode       Count(s) 
======== ========
BINARY_SUBSCR 3
BUILD_SLICE 1
BUILD_TUPLE 3
COMPARE_OP 1
LOAD_CONST 7
LOAD_FAST 3, 5 *** differs ***
LOAD_GLOBAL 2
RETURN_VALUE 1
STORE_FAST 0, 2 *** differs ***
STORE_SUBSCR 1

这里很明显,第二种更清晰的方法只使用了四个字节码,并且是简单、快速的 LOAD_FAST/STORE_FAST 变体。因此,静态分析表明没有特别的理由担心额外的内存分配或其他影响性能的副作用。

然后我构造了两个函数,它们彼此非常相似,反汇编程序显示的不同之处仅在于第二个函数有一个额外的 LOAD_FAST/STORE_FAST 对。我运行了它们 100,000,000 次,并比较了它们的运行时间。它们在 CPython 2.7.5 中相差超过 3 秒,在 PyPy 2.2.1(基于 Python 2.7.3)中相差约 1.5 秒。即使您将这些时间加倍(因为您有两对),很明显那些额外的加载/存储对不会减慢您的速度。

关于python - 效率与可读性 : obfuscation when using nested boolean index arrays,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26446302/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com