考虑例如
squares = *map((2).__rpow__, range(5)),
squares
# (0, 1, 4, 9, 16)
*squares, = map((2).__rpow__, range(5))
squares
# [0, 1, 4, 9, 16]
Run Code Online (Sandbox Code Playgroud)
因此,在所有其他条件相等的情况下,当我们在lhs上进行排序时,会得到一个列表,而当我们在rhss上进行布局时,会得到一个元组。
为什么?
这是设计使然吗,如果是的话,其原理是什么?否则,是否有任何技术原因?还是只是这样,没有特殊原因?
我目前正在努力更好地了解与内存相关的性能问题.我读到某处内存局部性对于读取比写入更重要,因为在前一种情况下,CPU必须实际等待数据,而在后一种情况下,它可以将它们运出并忘记它们.
考虑到这一点,我做了以下快速和肮脏的测试:
更新3(非python/numpy用户解释):
我在python/numpy中编写了一个脚本,它创建了一个N个随机浮点数和一个置换数组,即一个包含随机顺序的数字0到N-1的数组.然后重复(1)线性地读取数据阵列并将其写回由置换给出的随机访问模式中的新阵列,或者(2)以置换顺序读取数据阵列并线性地将其写入新阵列.
令我惊讶的是(2)似乎始终比(1)快.但是,我的脚本存在问题
此外,下面的一些答案/评论表明我原来的期望并不总是正确的,根据cpu缓存的细节,这两种情况可能会更快.
我现在放置的赏金适用于以下内容:
更新2:
鉴于迄今为止的评论和答案,我希望得到澄清
请以初学者友好的方式解释可能与此相关的各种缓存概念(参见@Trilarion的评论,@ Yann Vernier的回答).支持代码可能在C/cython/numpy/numba或python中.
或者,解释我明显不足的python实验的行为.
原文:
import numpy as np
from timeit import timeit
def setup():
global a, b, c
a = np.random.permutation(N)
b = np.random.random(N)
c = np.empty_like(b)
def fwd():
c = b[a]
def inv():
c[a] = b
N = 10_000
setup()
timeit(fwd, number=100_000)
# 1.4942631321027875
timeit(inv, number=100_000)
# 2.531870319042355
N = 100_000
setup()
timeit(fwd, number=10_000)
# 2.4054739447310567
timeit(inv, number=10_000)
# 3.2365565397776663
N = 1_000_000
setup() …
Run Code Online (Sandbox Code Playgroud) 为了举例,让我们假设我想子类化dict
并把所有的键都大写:
class capdict(dict):
def __init__(self,*args,**kwds):
super().__init__(*args,**kwds)
mod = [(k.capitalize(),v) for k,v in super().items()]
super().clear()
super().update(mod)
def __getitem__(self,key):
return super().__getitem__(key.capitalize())
def __setitem__(self,key,value):
super().__setitem__(key.capitalize(),value)
def __delitem__(self,key):
super().__detitem__(key.capitalize())
Run Code Online (Sandbox Code Playgroud)
这在一定程度上起作用,
>>> ex = capdict(map(reversed,enumerate("abc")))
>>> ex
{'A': 0, 'B': 1, 'C': 2}
>>> ex['a']
0
Run Code Online (Sandbox Code Playgroud)
但是,当然,仅适用于我记得要实现的方法,例如
>>> 'a' in ex
False
Run Code Online (Sandbox Code Playgroud)
不是想要的行为。
现在,填充可以从“核心”方法派生的所有方法的懒惰方式将混合在collections.abc.MutableMapping
. 只是,它在这里不起作用。我认为是因为所讨论的方法(__contains__
在示例中)已经由dict
.
有没有办法吃我的蛋糕并吃掉它?一些魔法让MutableMapping
只看到我已经覆盖的方法,以便它基于这些重新实现其他方法?
使用给定的fp数种类,例如float16,直接构造具有完全错误结果的和。例如,使用python / numpy:
import numpy as np
one = np.float16(1)
ope = np.nextafter(one,one+one)
np.array((ope,one,-one,-one)).cumsum()
# array([1.001, 2. , 1. , 0. ], dtype=float16)
Run Code Online (Sandbox Code Playgroud)
在这里,我们习惯于cumsum
强制天真的求和。留给自己的设备numpy
使用不同的求和顺序,会得到更好的答案:
np.array((ope,one,-one,-one)).sum()
# 0.000977
Run Code Online (Sandbox Code Playgroud)
以上是基于取消。为了排除此类示例,让我们仅允许使用非否定术语。对于幼稚的求和,给出具有非常错误的求和的示例仍然很容易。以下求和10 ^ 4个相同的项,每个项等于10 ^ -4:
np.full(10**4,10**-4,np.float16).cumsum()
# array([1.0e-04, 2.0e-04, 3.0e-04, ..., 2.5e-01, 2.5e-01, 2.5e-01],
dtype=float16)
Run Code Online (Sandbox Code Playgroud)
最后一项相差4倍。
同样,允许numpy
使用成对求和给出更好的结果:
np.full(10**4,10**-4,np.float16).sum()
# 1.0
Run Code Online (Sandbox Code Playgroud)
可以构造超过成对求和的和。选择低于分辨率为1的eps时,我们可以使用1,eps,0,eps,3x0,eps,7x0,eps,15x0,eps,...,但这涉及到疯狂的术语数量。
我的问题:仅使用float16和非否定项,就需要多少项来从成对求和中得出至少相差2倍的结果。
奖励:同样的问题是“积极”而不是“非消极”。可能吗?
假设我们要创建一系列类,这些类是总体概念的不同实现或特化。让我们假设某些派生属性有一个合理的默认实现。我们想把它放到一个基类中
class Math_Set_Base:
@property
def size(self):
return len(self.elements)
Run Code Online (Sandbox Code Playgroud)
因此,在这个相当愚蠢的示例中,子类将自动能够计算其元素
class Concrete_Math_Set(Math_Set_Base):
def __init__(self,*elements):
self.elements = elements
Concrete_Math_Set(1,2,3).size
# 3
Run Code Online (Sandbox Code Playgroud)
但是如果一个子类不想使用这个默认值怎么办?这不起作用:
import math
class Square_Integers_Below(Math_Set_Base):
def __init__(self,cap):
self.size = int(math.sqrt(cap))
Square_Integers_Below(7)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 3, in __init__
# AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)
我意识到有一些方法可以用一个属性覆盖一个属性,但我想避免这种情况。因为基类的目的是让用户的生活尽可能简单,而不是通过强加(从子类的狭隘角度来看)复杂和多余的访问方法来增加膨胀。
可以做到吗?如果不是,下一个最佳解决方案是什么?
当通过pysdl2与sdl2一起玩时,我注意到了一个奇怪的副作用,即一旦sdl脚本运行了不相关的窗口,这些窗口通常在移动时会变成半透明,现在却变得不透明。
如果不是因为the的感觉表明我在做根本上是错误的事情,我不会那么介意。
有人能启发我这里发生了什么吗?
这是我的脚本:
import sdl2
import sdl2.ext as se
import time
def main():
k = 2
event_buffer = (k * sdl2.SDL_Event)()
se.init()
window = se.Window("what the ?", size=(400, 300))
window.show()
while True:
window.refresh()
time.sleep(0.01)
sdl2.SDL_PumpEvents()
sdl2.SDL_PeepEvents(event_buffer, k, sdl2.SDL_GETEVENT,
sdl2.SDL_FIRSTEVENT, sdl2.SDL_LASTEVENT)
for event in event_buffer:
if not event.type:
continue
elif event.type == sdl2.SDL_QUIT:
se.quit()
break
else:
pass
event.type = 0
else:
continue
break
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
这是一个前后屏幕抓取:
KDE 5.45.0
没有运行sdl脚本的桌面的“系统设置”窗口,显示了相关的设置桌面Effects>Translucency
。注意,窗口是半透明的,因为在拍照时我正在拖动它。
相同,但运行的是sdl脚本。请注意,尽管我大力拖动窗口,但窗口仍然顽固不透明。
考虑以下简单测试:
import numpy as np
from timeit import timeit
a = np.random.randint(0,2,1000000,bool)
Run Code Online (Sandbox Code Playgroud)
让我们找到第一个的索引 True
timeit(lambda:a.argmax(), number=1000)
# 0.000451055821031332
Run Code Online (Sandbox Code Playgroud)
由于numpy
短路,这相当快。
它也适用于连续切片
timeit(lambda:a[1:-1].argmax(), number=1000)
# 0.0006490410305559635
Run Code Online (Sandbox Code Playgroud)
但是,似乎不连续的情况并非如此。我主要对查找最后一个感兴趣True
:
timeit(lambda:a[::-1].argmax(), number=1000)
# 0.3737605109345168
Run Code Online (Sandbox Code Playgroud)
更新:我的假设是观察到的减速是由于没有短路造成的,这是不准确的(感谢@Victor Ruiz)。实际上,在全
False
阵列的最坏情况下
b=np.zeros_like(a)
timeit(lambda:b.argmax(), number=1000)
# 0.04321779008023441
Run Code Online (Sandbox Code Playgroud)
我们仍然比不连续的情况快一个数量级。我已经准备好接受维克多(Victor)的解释,即真正的罪魁祸首是复制品(强迫使用复制品的时机
.copy()
是暗示性的)。之后,是否发生短路就不再重要了。
但是其他步长!= 1会产生类似的行为。
timeit(lambda:a[::2].argmax(), number=1000)
# 0.19192566303536296
Run Code Online (Sandbox Code Playgroud)
问题:为什么在最后两个示例中不进行复制就numpy
不会短路UPDATE ?
而且,更重要的是:是否有一种解决方法,即某种方法可以强制numpy
短更新, 而无需在非连续数组上也进行复制?
它似乎不是处理器的"位数"(32对64),特别是对这篇文章的评论:
好答案.正如我在上面的评论中提到的,我能够在使用64位ARM处理器的Raspberry Pi 3上复制@ suzep136的问题.知道为什么在64位架构上会出现溢出问题吗?我唯一能想到的是lapack/blas是为32位内核编译的; 我想我通过apt-get安装了numpy. - nrlakin
它也不是C中int的大小,例如在我的机器上:
>>> import numpy, ctypes
>>>
>>> ctypes.sizeof(ctypes.c_int)
4
>>> numpy.array([1]).dtype
dtype('int64')
Run Code Online (Sandbox Code Playgroud)
那么,它取决于什么?
编辑:还有另一位候选人,谢谢你们:
LAPACK在所有架构上使用32位整数 - ev-br
编辑:部分答案在这里.谢谢Goyo.我已经复制了它并使其成为CW,因此您可以添加更精细的点,例如PyPy或Jython中发生的事情.我也对这个选择是否有更深层次的原因感兴趣.
假设我想有大小的numpy的阵列(n,m)
,其中n
是非常大的,但有很多重复,即.0:n1
是相同的,n1:n2
是相同的等(有n2%n1!=0
,但不是规则的间隔).有没有办法只为每个重复项存储一组值,同时拥有整个数组的视图?
例:
unique_values = np.array([[1,1,1], [2,2,2] ,[3,3,3]]) #these are the values i want to store in memory
index_mapping = np.array([0,0,1,1,1,2,2]) # a mapping between index of array above, with array below
unique_values_view = np.array([[1,1,1],[1,1,1],[2,2,2],[2,2,2],[2,2,2], [3,3,3],[3,3,3]]) #this is how I want the view to look like for broadcasting reasons
Run Code Online (Sandbox Code Playgroud)
我计划将数组(视图)乘以其他一些大小的数组(1,m)
,并取这个产品的点积:
other_array1 = np.arange(unique_values.shape[1]).reshape(1,-1) # (1,m)
other_array2 = 2*np.ones((unique_values.shape[1],1)) # (m,1)
output = np.dot(unique_values_view * other_array1, other_array2).squeeze()
Run Code Online (Sandbox Code Playgroud)
输出是长度为1D的数组n
.
这看起来甚至是一个愚蠢的问题:当我建立一些自我调整如Python与PGO(或ATLAS或相信FFTW也做的话),计算机是否已被闲置的(不影响测量)或我可以打破Doom的时间吗?
来自python源代码发行版的链接README似乎认为这太微不足道了,但我真的不确定这一点.
python ×10
numpy ×5
python-3.x ×2
algorithm ×1
cpu-cache ×1
inheritance ×1
kde ×1
list ×1
performance ×1
profiling ×1
properties ×1
pysdl2 ×1
sdl ×1
splat ×1
sum ×1
tuples ×1
x86 ×1