Python Dictionary vs If Statement Speed

时间:2013-04-10 10:50:19

标签: python if-statement dictionary switch-statement

我发现有一些链接谈论切换案例在c ++中比其他更快,因为它可以在编译中进行优化。然后我找到了一些人们的建议,使用字典可能比If语句更快。然而,大多数谈话都是关于某些人的工作结束,最后讨论他们应该首先优化代码的其他部分,除非你做了数百万的其他事情,否则无关紧要。任何人都可以解释为什么会这样吗?

假设我有100个唯一的数字,这些数字将不断地传输到python代码中。我想检查它是哪个号码,然后执行一些操作。所以我可以做大量的if else,或者我可以将每个数字放在字典中。为了论证,让我们说它是一个单一的线程。

有人理解python和低级执行之间的层可以解释这是如何工作的吗?

谢谢:)

2 个答案:

答案 0 :(得分:13)

  

然而,大多数谈话都是关于某人的工作结束刚刚结束   讨论他们应该首先优化代码的其他部分   除非你做了数百万的其他事情,否则无关紧要。任何人都可以   解释为什么会这样?

一般情况下,如果你真的需要,你应该只打算优化代码,即如果程序的性能非常慢。

如果是这种情况,您应该使用分析器来确定哪些部件实际上导致了大多数问题。对于Python,cProfile模块对此非常有用。

  

有人理解python和低级别之间的层   执行可以解释这是如何工作的?

如果您想了解代码的执行方式,请查看dis模块。

一个简单的例子......

import dis

# Here are the things we might want to do
def do_something_a():
    print 'I did a'


def do_something_b():
    print 'I did b'


def do_something_c():
    print 'I did c'


# Case 1
def f1(x):
    if x == 1:
        do_something_a()
    elif x == 2:
        do_something_b()
    elif x == 3:
        do_something_c()


# Case 2
FUNC_MAP = {1: do_something_a, 2: do_something_b, 3: do_something_c}
def f2(x):
    FUNC_MAP[x]()


# Show how the functions execute
print 'Case 1'
dis.dis(f1)
print '\n\nCase 2'
dis.dis(f2)

...输出......

Case 1
 18           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       22

 19          12 LOAD_GLOBAL              0 (do_something_a)
             15 CALL_FUNCTION            0
             18 POP_TOP
             19 JUMP_FORWARD            44 (to 66)

 20     >>   22 LOAD_FAST                0 (x)
             25 LOAD_CONST               2 (2)
             28 COMPARE_OP               2 (==)
             31 POP_JUMP_IF_FALSE       44

 21          34 LOAD_GLOBAL              1 (do_something_b)
             37 CALL_FUNCTION            0
             40 POP_TOP
             41 JUMP_FORWARD            22 (to 66)

 22     >>   44 LOAD_FAST                0 (x)
             47 LOAD_CONST               3 (3)
             50 COMPARE_OP               2 (==)
             53 POP_JUMP_IF_FALSE       66

 23          56 LOAD_GLOBAL              2 (do_something_c)
             59 CALL_FUNCTION            0
             62 POP_TOP
             63 JUMP_FORWARD             0 (to 66)
        >>   66 LOAD_CONST               0 (None)
             69 RETURN_VALUE


Case 2
 29           0 LOAD_GLOBAL              0 (FUNC_MAP)
              3 LOAD_FAST                0 (x)
              6 BINARY_SUBSCR
              7 CALL_FUNCTION            0
             10 POP_TOP
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE

...所以很容易看出哪个函数必须执行最多的指令。

至于哪个实际上更快,这是你必须通过分析代码来检查的。

答案 1 :(得分:5)

if / elif / else结构逐个比较它给一个可能值序列的密钥,直到它在一些if语句的条件下找到匹配,然后从if内部读取它应该执行的内容块。这可能需要很长时间,因为必须为每次查找进行如此多的检查(n/2平均n个可能的值)。

if语句序列比switch语句更难以优化的原因是条件检查(C ++中parens里面的内容)可能会改变下一次检查中涉及的某个变量的状态,所以你必须按顺序完成它们。对switch语句的限制消除了这种可能性,因此顺序无关紧要(我认为)。


Python词典are implemented as hash tables。这个想法是这样的:如果你可以处理任意大数并拥有无限的RAM,你可以创建一个庞大的函数指针数组,只需将你的查找值转换为整数并将其作为索引。查找几乎是即时的。

当然,你不能这样做,但你可以创建一个可管理长度的数组,将查找值传递给hash function(生成一些整数,取决于查找值),然后%您的结果与数组的长度,以获得该数组范围内的索引。这样,查找需要花费尽可能多的时间来调用哈希函数一次,获取模数,并跳转到索引。如果不同可能的查找值的数量足够大,则与那些n/2条件检查相比,散列函数的开销可以忽略不计。

(实际上,由于许多不同的查找值将不可避免地映射到相同的索引,因此它并不那么简单。您必须检查并解决可能的冲突,这可以通过多种方式完成。仍然是,它如上所述。)