我最近比较了collections.Counter
和{
使用line_profiler
的瓶颈似乎是isinstance(iterable, collections.Mapping)
-签入{
%load_ext line_profiler # IPython
lst = list(range(1000))
%lprun -f Counter.update Counter(lst)
给我:
^{pr2}$因此,即使长度为1000 Iterable,也需要15%以上的时间。对于更短的iterables(例如20个项目,它增加到60%)。在
我最初认为它与collections.Mapping
如何使用__subclasshook__
有关,但是在第一次isinstance
检查之后,这个方法就不再被调用了。那么为什么检查isinstance(iterable, Mapping)
这么慢?在
性能实际上只与ABCMeta's ^{} 中的一个检查集合有关,该集合由^{} 调用。在
底线是,这里所看到的低性能并不是由于某些缺少优化的结果,而是由于
isinstance
的结果,抽象基类是Python级别的操作,如Jim所述。正结果和负结果都会被缓存,但是即使使用缓存的结果,您也需要在每个循环中查看几微秒,以便遍历ABCMeta类的__instancecheck__
方法中的条件。在示例
考虑一些不同的空结构。在
我们可以看到性能差异-是什么原因造成的?在
对于dict
^{pr2}$列表
我们可以看到,对于dict,映射抽象类
_abc_cache
包括我们的dict,所以要提前检查短路。显然,对于一个列表,肯定的缓存不会被命中,但是映射的
_abc_negative_cache
包含列表类型以及现在的pd系列类型,这是使用
%timeit
多次调用isinstance
的结果。在我们没有命中负缓存的情况下(比如一个系列的第一次迭代),Python使用常规的子类check with这可能会慢得多,通过子类钩子和递归子类检查seen here,然后缓存结果以备后续加速。在
相关问题 更多 >
编程相关推荐