这是我几天前回答的一个后续问题。 编辑:
该问题的OP似乎已经使用了我发布给他的代码来问同样的问题,但是我没有意识到。道歉。提供的答案是不同的!
我基本上观察到:
>>> def without_else(param=False):
... if param:
... return 1
... return 0
>>> def with_else(param=False):
... if param:
... return 1
... else:
... return 0
>>> from timeit import Timer as T
>>> T(lambda : without_else()).repeat()
[0.3011460304260254, 0.2866089344024658, 0.2871549129486084]
>>> T(lambda : with_else()).repeat()
[0.27536892890930176, 0.2693932056427002, 0.27011704444885254]
>>> T(lambda : without_else(True)).repeat()
[0.3383951187133789, 0.32756996154785156, 0.3279120922088623]
>>> T(lambda : with_else(True)).repeat()
[0.3305950164794922, 0.32186388969421387, 0.3209099769592285]
…或者换句话说:else
无论if
是否触发条件,拥有该子句都会更快。
我认为这与两者生成的不同字节码有关,但是有人能详细确认/解释吗?
编辑: 似乎不是每个人都可以重现我的时间安排,所以我认为在我的系统上提供一些信息可能有用。我正在运行默认安装了python的Ubuntu 11.10
64位。python
生成以下版本信息:
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
以下是Python 2.7中反汇编的结果:
>>> dis.dis(without_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
4 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
>>> dis.dis(with_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
5 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
这纯粹是猜测,我还没有找到一种简单的方法来检查它是否正确,但是我有一个理论适合您。
我尝试了您的代码并获得了相同的结果,但without_else()
反复地比with_else()
:
>>> T(lambda : without_else()).repeat()
[0.42015745017874906, 0.3188967452567226, 0.31984281521812363]
>>> T(lambda : with_else()).repeat()
[0.36009842032996175, 0.28962249392031936, 0.2927151355828528]
>>> T(lambda : without_else(True)).repeat()
[0.31709728471076915, 0.3172671387005721, 0.3285821242644147]
>>> T(lambda : with_else(True)).repeat()
[0.30939889008243426, 0.3035132258429485, 0.3046679117038593]
考虑到字节码是相同的,唯一的区别是函数的名称。尤其是时序测试会在全局名称上进行查找。尝试重命名without_else()
,区别消失:
>>> def no_else(param=False):
if param:
return 1
return 0
>>> T(lambda : no_else()).repeat()
[0.3359846013948413, 0.29025818923918223, 0.2921801513879245]
>>> T(lambda : no_else(True)).repeat()
[0.3810395594970828, 0.2969634408842694, 0.2960104566362247]
我的猜测是,without_else
它与其他内容发生了哈希冲突,globals()
因此全局名称查找稍微慢一些。
编辑 :具有7个或8个键的字典可能具有32个插槽,因此在此基础上,without_else
哈希值与__builtins__
:
>>> [(k, hash(k) % 32) for k in globals().keys() ]
[('__builtins__', 8), ('with_else', 9), ('__package__', 15), ('without_else', 8), ('T', 21), ('__name__', 25), ('no_else', 28), ('__doc__', 29)]
要阐明哈希如何工作:
__builtins__
散列为-1196389688,这减小了表大小(32)的模数,这意味着它存储在表的#8插槽中。
without_else
哈希到505688136,将模32的值降低为8,因此发生冲突。要解决此问题,Python计算:
从…开始:
j = hash % 32
perturb = hash
重复此过程,直到找到可用的插槽:
j = (5*j) + 1 + perturb;
perturb >>= 5;
use j % 2**i as the next table index;
这使它可以用作下一个索引17。幸运的是,它是免费的,因此循环仅重复一次。哈希表的大小是2的幂,哈希表的大小也是2的幂2**i
,i
是哈希值使用的位数j
。
对该表的每个探测都可以找到以下之一:
插槽为空,在这种情况下,探测停止,并且我们知道该值不在表中。
该广告位尚未使用,但已在过去使用过,在这种情况下,我们尝试使用如上计算的下一个值。
插槽已满,但存储在表中的完整哈希值与我们要查找的键的哈希值不同(在__builtins__
vs的情况下就是这样without_else
)。
插槽已满,并且正好具有我们想要的哈希值,然后Python检查以查看键和我们正在查找的对象是否是同一对象(在这种情况下,这是因为可能是标识符的短字符串已被插入,所以相同的标识符使用完全相同的字符串)。
最终,当插槽已满时,哈希完全匹配,但是键不是同一对象,然后Python才会尝试比较它们是否相等。这相对较慢,但实际上不应该进行名称查找。
问题内容: 在Pro Spring 3丛书的第4章-Spring中的IOC和DI简介-第59页中的“ Setter注入与构造方法注入”部分中,一段说 包括Spring,提供了一种机制来确保在使用Setter Injection时定义了所有依赖关系,但是通过使用Constructor Injection,你可以以与容器无关的方式声明对依赖关系的要求。” 你能举例说明一下吗 问题答案: 将必需的依赖项
问题内容: 我刚刚开始尝试在Node.js应用程序上实现Promises。现在,我正在检查用户和密码是否存在,然后使用mongodb来查找未找到用户的用户和密码,它会设置promise.reject(),但是它返回的promise太早了,它仍然处于未决状态。如果有人可以帮助或给我有关如何重构的想法,将不胜感激。 https://gist.github.com/joshbedo/8957056 问题
问题内容: 我有一个简单的任务:计算每个字母在一个字符串中出现的次数。我已经使用了它,但是在一个论坛上我看到了使用/比每个字母都要慢得多的信息。我认为它只能在字符串中进行一次遍历,而解决方案则必须遍历该字符串四次(在这种情况下)。为什么这么慢? 问题答案: 允许您计算任何可哈希对象,而不仅仅是子字符串。两种解决方案都是-time。您的测量结果表明,迭代和散列单个字符的开销大于运行4倍。 可以 使用
我一直在寻找答案,但我没有找到它,我需要在我继续我的测试之前弄明白。 我正在通过安装bash创建一个基于Alpine的映像,如下图所示: Hello.sh 我可以在不向docker-compose.yml添加的情况下访问,所以Alpine的映像中一定有我不理解和希望理解的东西。
首先要明确一点的是,没有最好,只有最合适。前端框架并不是所谓 注:本文一些数据收集时间为16-12-31 因为大部分教程都是付费的,所以教程数量的多少在侧面反映了框架的市场需求,如果框架冷门用的人不多,那必然不会有人去为其开发教程。
我正在使用Java Mission Control分析一个Java应用程序,它在飞行记录的主页上说:“尽管CPU负载很高,但该记录包含的分析样本很少。因此,分析数据可能不相关。” 它似乎在说真话。我要求它每10毫秒采样3分钟,应该是18000个样本,但我只看到996个样本。