2013-04-10 58 views
8

我发现了一些链接,谈论开关案例在C++中比在其他语言中更快,因为它可以在编译中进行优化。然后我发现一些人使用字典的建议可能比If语句更快。然而,大部分谈话都是关于某个人的工作,最后只是讨论他们应该首先优化代码的其他部分,除非你做了数以百万计的其他部分,否则这将不重要。任何人都可以解释为什么这是?Python字典与如果语句速度

说我有100个独特的数字,将不断流入python代码。我想检查它是哪个号码,然后执行一些操作。所以我可以做很多其他的事情,或者我可以把每个数字都放在字典中。为了争辩,让我们说它是一个单一的线程。

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

谢谢:)

+0

*“我想检查它是哪个号码”*这是什么意思?你的问题可能要更具体一些。 Python如果语句速度很慢,可以通过在C级保持工作来优化Python代码,但这是非常特定的情况,而且您的问题太宽泛。 – jamylak 2013-04-10 10:52:01

+0

所以如果我知道它将是一个数字0到100.我想检查它是否为0,然后做一些事情,如果它是1做别的事情。 – user1938107 2013-04-10 10:54:32

+0

有这么几个号码(0-100),如果你认为你需要优化,然后做一个表,这样就可以直接去回答。但是,您是否真的先尝试过“if”或字典方案?那是......你是否有充分的理由相信超越这两者之一的优化是必要的? – mah 2013-04-10 10:57:14

回答

10

然而,大部分谈话是关于某人的工作结束刚刚结束 了讨论,他们应该优化代码的其他部分第一 ,它不会不管,除非你做的数以百万计的如果别的。任何人都可以解释这是为什么吗?

通常情况下,如果您确实需要优化代码,也就是说如果程序的性能无法缓慢地运行,您应该只会费心去优化代码。

如果是这样的话,你应该使用一个分析器,以确定哪些部分实际上是造成最多的问题。对于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 

...所以它是很容易看到哪些函数来执行大多数指令。

至于这实际上要快,这东西你必须通过剖析代码检查。

5

的IF/ELIF/else结构,直到它的一些if语句的条件找到匹配比较它是由一个给可能的值一个序列中的键,然后读什么应该从if块内部执行。这可能需要很长时间,因为必须为每个查找进行太多检查(平均值为n/2,对于n可能的值)。

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


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

你不能做到这一点,当然,但你可以创建一些管理长度的阵列,通过查找值的hash function(产生一些整数,根据查找值),那么你的%导致您的数组长度在该数组的范围内获取索引。这样,查找需要尽可能多的时间来调用哈希函数一次,取模数,并跳转到索引。如果不同可能的查找值的数量足够大,那么与那些条件检查相比,哈希函数的开销变得可以忽略不计。 (实际上,由于许多不同的查找值将不可避免地映射到相同的索引,所以并不那么简单,您必须检查并解决可能的冲突,这可以通过多种方式完成。它是如上文所述)。

+0

这是有道理的,所以即时考虑是否存在某个类,并且该类有1到100个值,并且我创建了这个类的一个实例,myclass.ReceivedValue,它将在同一个O中的类下调用该函数)作为哈希表...也许? – user1938107 2013-04-10 13:03:47

+0

如果你的意思是每个查找值天生与[1100]唯一的索引,它是用来在数组访问一个元素相关联,那么* *概念是,虽然在实践中解决冲突,使哈希表稍微慢一些。 – alcedine 2013-04-10 13:33:21