Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么使用np.empty的分配不是O(1)_Python_Numpy_Time Complexity - Fatal编程技术网

Python 为什么使用np.empty的分配不是O(1)

Python 为什么使用np.empty的分配不是O(1),python,numpy,time-complexity,Python,Numpy,Time Complexity,官方numpy 返回给定形状和类型的新数组,而不初始化条目 对于np.empty,这意味着创建(分配)此数组所需的时间为O(1),但timeit中的一些简单测试表明情况并非如此: >>> timeit.timeit(lambda: np.empty(100000000 ), number=10000) 0.2733485999999914 >>> timeit.timeit(lambda: np.empty(1000000000), number=10000)

官方
numpy

返回给定形状和类型的新数组,而不初始化条目

对于
np.empty
,这意味着创建(分配)此数组所需的时间为O(1),但
timeit
中的一些简单测试表明情况并非如此:

>>> timeit.timeit(lambda: np.empty(100000000 ), number=10000)
0.2733485999999914
>>> timeit.timeit(lambda: np.empty(1000000000), number=10000)
0.8293009999999867

作为附带问题,未触及的
np.empty
数组中存在哪些值?它们都是非常小的值,但我希望它们是内存中该地址的任何值。(示例数组:
np.empty(2)=数组([-6.42940774e-036,2.074094447E-117])
。这些看起来与存储在内存中的内容完全不同)

首先,我尝试在不同大小的机器上重现这种行为。以下是原始结果:

np.empty(10**1)351ns±23.7ns/循环(7次运行,每个循环1000000次)
np.空(10**2)#406纳秒±1.44纳秒/圈(7次运行,每次1000000圈)
np.空(10**3)351ns±5.8ns/圈(7次运行,每次1000000圈)
np.空(10**4)616纳秒±1.56纳秒/圈(7次运行,每次1000000圈)
np.空(10**5)#620纳秒±2.83纳秒/圈(7次运行,每次1000000圈)
np.空(10**6)#9.61µs±34.2 ns/圈(7次运行,每次100000圈)
np.空(10**7)#11.1µs±17.6 ns/圈(7次运行,每个100000圈)
np.空(10**8)#22.1µs±173 ns/圈(7次运行,每次10000圈)
np.空(10**9)#62.8µs±220 ns/圈(7次运行,每次10000圈)
np.empty(10**10)#=>内存错误
因此,您是对的:这是没有做到的是
O(1)
(至少在我的Windows机器和您的系统上也是如此)。请注意,在如此短的时间内无法(急切地)初始化这些值,因为这意味着RAM吞吐量超过127 TB/s,而我的机器上显然没有

对于np.empty,这意味着创建(分配)此数组所需的时间为O(1)

O(1)
中进行分配的假设并不完全正确。为了验证这一点,我构建了一个简单的C程序,执行一个简单的
malloc
+
free
循环,并测量了计时。以下是原始结果:

/malloc.exe 10#平均时间:41.815纳秒(在一次运行中,每个循环1000000次)
./malloc.exe 100#平均时间:45.295纳秒(在一次运行中,每个循环1000000次)
./malloc.exe 1000#平均时间:47.400纳秒(在一次运行中,每个循环1000000次)
./malloc.exe 10000#平均时间:122.457纳秒(在一次运行中,每个循环1000000次)
./malloc.exe 100000#平均时间:123.032纳秒(在一次运行中,每个循环1000000次)
./malloc.exe 1000000#平均时间:8.351 us(在一次运行中,每个循环1000000次)
./malloc.exe 10000000#平均时间:9.342 us(一次运行,每个循环100000次)
./malloc.exe 100000000#平均时间:18.972 us(一次运行,每个循环10000次)
./malloc.exe 1000000000#平均时间:64.527 us(一次运行,每个循环10000次)
./malloc.exe 1000000000#=>内存错误
如您所见,结果与Numpy的结果相匹配(除了较小的结果,这是由于在CPython中调用Python函数的开销造成的)。因此,问题不在于Numpy,而在于标准libc或操作系统本身中的分配算法

作为一个附带问题,在未触及的np.empty数组中存在哪些值

它是未初始化的数据。在实践中,它通常是零初始化的(但并不总是),因为主流平台出于安全原因对分配的内存进行了清理(以便密码等关键数据在以前存储在另一个进程的内存中时不会泄漏)您不应依赖此


malloc
计时的更深入解释: 如你所见,10万件物品的分配与100万件物品的分配之间存在差距。这可以通过使用快速用户空间分配器来解释(在Unix和Linux系统上调用):当数据较小时,大多数主流平台的libc不会直接向操作系统请求内存。而是使用一个快速的预分配的本地内存池。实际上,在大多数主流平台上,预先分配了多个不同大小的池,libc根据分配的大小选择“正确的池”,因此小数据量的时间会有所变化。请注意,执行此过程是为了提高分配速度,同时考虑到。由于内核调用(如
mmap
)非常昂贵(在我的机器上至少需要几微秒),因此此策略的速度要快得多

此外,大多数操作系统(OS)都有多个内存池。Linux、MacOS和Windows将虚拟内存拆分为小页面(通常为4KB)。由于在处理GB/TB的分配数据时,处理太小的页面会带来很大的开销,因此这些操作系统还提供称为超级页面的大页面或大页面(通常为2MB到几GB)。操作系统中采用的路径可能会因分配的内存量而发生变化,大多数操作系统都针对分配小块虚拟内存而不是大块虚拟内存进行了优化

请注意,用于管理系统内存的数据结构的大小通常受到RAM大小的限制,RAM的大小在运行时通常是恒定的。此外,在给定操作系统中用于管理内存碎片的算法的复杂性在理论上可能是
O(1)
(或接近该复杂性)。因此,一些人认为分配/释放数据是在固定时间内完成的。但这是有争议的,因为我们应该考虑实际结果,而不仅仅是理论上的
渐近界


有关更多信息,请参阅以下帖子:

在这种情况下