我刚刚注意到,我的一个脚本的执行时间几乎一半,只需将乘法改为除法。
为了研究这个问题,我写了一个小例子:

import numpy as np
import timeit

# uint8 array
arr1 = np.random.randint(0, high=256, size=(100, 100), dtype=np.uint8)

# float32 array
arr2 = np.random.rand(100, 100).astype(np.float32)
arr2 *= 255.0


def arrmult(a):
    """
    mult, read-write iterator
    """
    b = a.copy()
    for item in np.nditer(b, op_flags=["readwrite"]):
        item[...] = (item + 5) * 0.5

def arrmult2(a):
    """
    mult, index iterator
    """
    b = a.copy()
    for i, j in np.ndindex(b.shape):
        b[i, j] = (b[i, j] + 5) * 0.5

def arrmult3(a):
    """
    mult, vectorized
    """
    b = a.copy()
    b = (b + 5) * 0.5

def arrdiv(a):
    """
    div, read-write iterator
    """
    b = a.copy()
    for item in np.nditer(b, op_flags=["readwrite"]):
        item[...] = (item + 5) / 2

def arrdiv2(a):
    """
    div, index iterator
    """
    b = a.copy()
    for i, j in np.ndindex(b.shape):
           b[i, j] = (b[i, j] + 5)  / 2

def arrdiv3(a):
    """
    div, vectorized
    """
    b = a.copy()
    b = (b + 5) / 2




def print_time(name, t):
    print("{: <10}: {: >6.4f}s".format(name, t))

timeit_iterations = 100

print("uint8 arrays")
print_time("arrmult", timeit.timeit("arrmult(arr1)", "from __main__ import arrmult, arr1", number=timeit_iterations))
print_time("arrmult2", timeit.timeit("arrmult2(arr1)", "from __main__ import arrmult2, arr1", number=timeit_iterations))
print_time("arrmult3", timeit.timeit("arrmult3(arr1)", "from __main__ import arrmult3, arr1", number=timeit_iterations))
print_time("arrdiv", timeit.timeit("arrdiv(arr1)", "from __main__ import arrdiv, arr1", number=timeit_iterations))
print_time("arrdiv2", timeit.timeit("arrdiv2(arr1)", "from __main__ import arrdiv2, arr1", number=timeit_iterations))
print_time("arrdiv3", timeit.timeit("arrdiv3(arr1)", "from __main__ import arrdiv3, arr1", number=timeit_iterations))

print("\nfloat32 arrays")
print_time("arrmult", timeit.timeit("arrmult(arr2)", "from __main__ import arrmult, arr2", number=timeit_iterations))
print_time("arrmult2", timeit.timeit("arrmult2(arr2)", "from __main__ import arrmult2, arr2", number=timeit_iterations))
print_time("arrmult3", timeit.timeit("arrmult3(arr2)", "from __main__ import arrmult3, arr2", number=timeit_iterations))
print_time("arrdiv", timeit.timeit("arrdiv(arr2)", "from __main__ import arrdiv, arr2", number=timeit_iterations))
print_time("arrdiv2", timeit.timeit("arrdiv2(arr2)", "from __main__ import arrdiv2, arr2", number=timeit_iterations))
print_time("arrdiv3", timeit.timeit("arrdiv3(arr2)", "from __main__ import arrdiv3, arr2", number=timeit_iterations))

这将打印以下时间:
uint8 arrays
arrmult   : 2.2004s
arrmult2  : 3.0589s
arrmult3  : 0.0014s
arrdiv    : 1.1540s
arrdiv2   : 2.0780s
arrdiv3   : 0.0027s

float32 arrays
arrmult   : 1.2708s
arrmult2  : 2.4120s
arrmult3  : 0.0009s
arrdiv    : 1.5771s
arrdiv2   : 2.3843s
arrdiv3   : 0.0009s

我一直认为乘法计算起来比除法便宜。然而,对于uint8这是否与这样一个事实有关,即* 0.5必须计算浮点中的乘法,然后将结果强制转换回整数?
至少对于浮点数来说,乘法似乎比除法快。这通常是真的吗?
为什么uint8中的乘法比float32中的乘法更容易扩展?我认为8位无符号整数应该比32位浮点更快计算?!
有人能“揭开”这个秘密吗?
编辑:为了获得更多的数据,我还包括了向量化函数(如建议的)和添加的索引迭代器。矢量化函数更快,因此不具有可比性。然而,如果矢量化函数的timeit_iterations设置得更高,则结果表明,uint8float32的乘法速度更快。我想这更让人困惑?!
也许乘法实际上总是比除法快,但是for循环中的主要性能泄漏不是算术运算,而是循环本身。尽管这不能解释为什么循环在不同的操作中表现不同。
edit2:like@jotasi already stated,we are looking for a full explanation ofdivisionvs.multiplicationandint(oruint8)vs.float(orfloat32)。此外,解释矢量化方法和迭代器的不同趋势也很有趣,就像在矢量化情况下一样,除法似乎较慢,而在迭代器情况下则更快。

最佳答案

问题是你的假设,你测量除法或乘法所需的时间,这是不正确的。您正在测量除法或乘法所需的开销。
我们必须查看准确的代码来解释每种效果,因为不同版本的效果不同。这个答案只能给出一个想法,一个必须考虑的问题。
问题是,在python中,simpleint一点也不简单:它是一个必须在垃圾收集器中注册的真实对象,它的大小随它的值而增长-对于您必须支付的所有费用:例如,需要一个8位整数24字节的内存!类似的情况也适用于python float。
另一方面,numpy数组由简单的C样式整数/浮点数组成,没有开销,可以节省大量内存,但在访问numpy数组的元素时会为此付出代价。a[i]意味着:必须构造一个python整数,并在垃圾收集器中注册,而且只能使用它-这会产生很多开销。
考虑此代码:

li1=[x%256 for x in xrange(10**4)]
arr1=np.array(li1, np.uint8)

def arrmult(a):
    for i in xrange(len(a)):
        a[i]*=5;

arrmult(li1)arrmult(arr1)快25,因为列表中的整数已经是python in t,不必创建!计算时间中最大的一部分是创建对象所需要的——其他的一切几乎都可以忽略不计。
让我们看一下您的代码,首先是乘法:
def arrmult2(a):
    ...
    b[i, j] = (b[i, j] + 5) * 0.5

对于uint8,必须发生以下情况(为了简单起见,我忽略了+5):
必须创建python int
它必须强制转换为float(创建python float),以便能够进行float乘法。
并返回到python int或/和uint8
对于float32,要做的工作较少(乘法不需要花费太多):
1。创建了一个python float
2。后抛浮筒32。
所以浮动版本应该更快。
现在让我们来看看这个部门:
def arrdiv2(a):
    ...
    b[i, j] = (b[i, j] + 5)  / 2

这里的陷阱是:所有的操作都是整数操作。因此,与乘法相比,不需要强制转换为python float,因此与乘法相比,我们的开销更少。对于unint8,除法比乘法“快”。
然而,对于float32来说,除法和乘法的速度是一样的快/慢,因为在这种情况下几乎没有什么变化——我们仍然需要创建一个python float。
现在,矢量化版本:它们使用C样式的“原始”float32s/uint8s,不需要转换(及其成本!)到引擎盖下相应的python对象。为了得到有意义的结果,您应该增加迭代的次数(现在运行时间太小,无法确定地说些什么)。
float32的除法和乘法可以有相同的运行时间,因为我希望numpy用2替换除法,然后乘以0.5(但要确保有人必须查看代码)。
uint8的乘法应该较慢,因为每个uint8整数必须在乘0.5之前强制转换为浮点,而不是在乘0.5之后强制转换回uint8。
对于uint8情况,numpy不能用0.5替换乘2的除法,因为它是整数除法。对于许多体系结构来说,整数除法比浮点乘法慢——这是最慢的矢量化操作。
附言:我不会对成本的乘法与除法有太多的顾虑——还有太多其他的东西会对性能产生更大的影响。例如,创建不必要的临时对象,或者如果numpy数组很大并且不适合缓存,那么内存访问将成为瓶颈——您将看到乘法和除法之间没有任何区别。

关于python - NumPy表现:uint8 vs. float 和乘法与除法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39104562/

10-12 22:04