【问题标题】:Python Dictionary vs If Statement SpeedPython 字典 vs If 语句速度
【发布时间】:2013-04-02 04:06:54
【问题描述】:

我发现了一些链接,它们谈论在 c++ 中的 switch case 比 if else 更快,因为它可以在编译中进行优化。然后我发现了一些人们认为使用字典可能比使用 If 语句更快的建议。然而,大部分对话都是关于某人的工作,最终只是讨论他们应该首先优化代码的其他部分,除非你做了数百万个 if else,否则这无关紧要。谁能解释这是为什么?

假设我有 100 个唯一数字,它们将不断地流入 Python 代码。我想检查它是哪个数字,然后执行一些操作。所以我可以做大量的 if else,或者我可以把每个数字放在字典里。为了争论起见,可以说它是一个单线程。

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

谢谢:)

【问题讨论】:

  • "我想检查它是哪个数字"这是什么意思?您可能必须对您的问题更加具体。 Python if 语句可能很慢,有一些方法可以通过保持 C 级工作来优化 python 代码,但是这是非常具体的,而且你的问题太宽泛了。
  • 这么少的数字(0-100),如果你认为需要优化,那就做个表格,这样你就可以直接找到答案了。但是,您实际上是否首先尝试过“如果”或字典场景?也就是说...您是否有充分的理由相信超出上述任何一项的优化是必要的?
  • 如果你“线性”排列 if 语句(即没有嵌套),它应该具有线性时间复杂度,而 dict 通常具有 O(log n) 时间复杂度。
  • @AndreHolzner 你到底为什么说字典有 O(log n) 复杂度?

标签: python if-statement dictionary switch-statement


【解决方案1】:

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

一般来说,您应该只在真正需要时才去优化代码,即如果程序的性能慢得无法使用。

如果是这种情况,您应该使用分析器来确定哪些部分实际上导致了最多的问题。对于 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

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

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

【讨论】:

    【解决方案2】:

    if/elif/else 结构将其赋予的键与一系列可能的值逐一进行比较,直到在某些 if 语句的条件中找到匹配项,然后从 if 内部读取它应该执行的内容堵塞。这可能需要很长时间,因为每次查找都必须进行如此多的检查(平均为n/2,对于n 可能的值)。

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


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

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

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

    【讨论】:

    • 这是有道理的,所以我想如果有一些类,并且该类有 1 到 100 个值,我创建了这个类的一个实例,比如 myclass.ReceivedValue ,它将调用下面的函数与哈希表在同一 O() 中的类...也许?
    • 如果您的意思是每个查找值天生就与 [1,100] 中的唯一索引相关联,该索引用于访问数组中的元素,那么 在概念上 是的,不过在实践中,冲突解决会使哈希表稍微变慢。
    猜你喜欢
    • 1970-01-01
    • 2014-11-20
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多