如何在python中加快numpy数组填充?

| 我正在尝试使用以下代码填充预分配的字节数组:
# preallocate a block array
dt = numpy.dtype(\'u8\')
in_memory_blocks = numpy.zeros(_AVAIL_IN_MEMORY_BLOCKS, dt)

...

# write all the blocks out, flushing only as desired
blocks_per_flush_xrange = xrange(0, blocks_per_flush)
for _ in xrange(0, num_flushes):
    for block_index in blocks_per_flush_xrange:
        in_memory_blocks[block_index] = random.randint(0, _BLOCK_MAX)

    print(\'flushing bytes stored in memory...\')

    # commented out for SO; exists in actual code
    # removing this doesn\'t make an order-of-magnitude difference in time
    # m.update(in_memory_blocks[:blocks_per_flush])

    in_memory_blocks[:blocks_per_flush].tofile(f)
一些要点:
num_flushes
低,在4-10附近
blocks_per_flush
是一个大数,数量级为数百万
in_memory_blocks
可以是一个相当大的缓冲区(我将其设置为低至1MB,高至100MB),但是时间安排非常合理...
_BLOCK_MAX
是8字节无符号整数的最大值
m
是a6ѭ 使用上面的代码生成1MB需要大约1s; 500MB需要376秒。相比之下,我使用rand()的简单C程序可以在8s内创建500MB文件。 如何在上述循环中提高性能?我敢肯定,我会忽略一些导致运行时如此巨大差异的明显原因。     
已邀请:
由于您要分配连续的块,因此您应该能够执行以下操作(完全摆脱内部循环):
for _ in xrange(0, num_flushes):
    in_memory_blocks[:blocks_per_flush] = numpy.random.randint(
            0, _BLOCK_MAX+1, blocks_per_flush)

    print(\'flushing bytes stored in memory...\')

    # commented out for SO; exists in actual code
    # removing this doesn\'t make an order-of-magnitude difference in time
    # m.update(in_memory_blocks[:blocks_per_flush])

    in_memory_blocks[:blocks_per_flush].tofile(f)
这使用了“ 8”函数,该函数分配了整个内存块,并用随机整数填充它(请注意,塞巴斯蒂安·J.F。Sebastian在下面的评论中,分别是“ 8”和“ 10”)。 (据我所知)没有办法使用numpy随机例程填充预分配的数组。另一个问题是numpy \的randint返回int64数组。如果需要其他大小的整数,则可以使用numpy输入方法,例如numpy.uint8。如果要让randint覆盖类型的整个范围,请使用@J。 F. Sebastian的下面使用numpy.random.bytes的方法将是最好的(几乎在任何情况下都可以!)。 但是,简单的测试显示合理的时间(与C代码相同的数量级)。以下代码使用numpy方法测试分配20,000,000个随机整数的uint8数组的时间:
from timeit import Timer
t = Timer(stmt=\'a=numpy.uint8(numpy.random.randint(0, 100, 20000000))\',
        setup=\'import numpy\')
test_runs = 50
time = t.timeit(test_runs)/test_runs
print time
我在使用4年的Core2笔记本电脑上每次分配大约要花费0.7秒的时间(它运行50次,因此运行整个测试将花费更长的时间)。每个20,000,000个随机uint8整数的分配时间为0.7 s,因此我希望整个500MB的时间大约为20 s。 更多的内存意味着您可以一次分配更大的块,但是当您只需要8个块时,您仍然有效地浪费了时间为每个int分配和写入64位(我还没有量化这种效果)。如果仍然不够快,则可以使用numpy ctypes接口调用C实现。这真的很容易使用,并且与纯C语言相比,您几乎不会放慢速度。 一般的带回家的信息是,对于numpy,请始终尝试使用它们存在的numpy例程,并记住使用ctypes退回到C不会太痛苦。总的来说,这种方法可以非常有效地使用python,而数值处理的速度却非常慢。 编辑:我刚刚想到的其他事情:如上所实现,我认为您会制作其他不必要的副本。如果
in_memory_blocks
的长度为
blocks_per_flush
,那么最好将其分配为
numpy.random.randint
的返回值,而不是将其分配给某个子数组(一般情况下必须是副本)。所以:
in_memory_blocks = numpy.random.randint(0, _BLOCK_MAX+1, blocks_per_flush)
而不是:
in_memory_blocks[:blocks_per_flush] = numpy.random.randint(
        0, _BLOCK_MAX+1, blocks_per_flush)
但是,在计时了之后,第一种情况并不会导致速度显着提高(仅约2%),因此可能不必担心太多。我想绝大多数时间是花在生成随机数上(这是我期望的)。     
由于
0.._BLOCK_MAX
涵盖了
numpy.uint8
的所有可能值(我假设
numpy.dtype(\'u8\')
(即
numpy.uint64
是错字)),您可以使用:
import numpy as np

for _ in xrange(0, num_flushes):
    in_memory_blocks = np.frombuffer(np.random.bytes(blocks_per_flush),
                                     dtype=np.uint8)

    print(\'flushing bytes stored in memory...\')
    # ...
这个变体比@hgomersall的变体快8倍:
$ python -mtimeit -s\'import numpy as np\' \'
>     np.uint8(np.random.randint(0,256,20000000))\'
10 loops, best of 3: 316 msec per loop

$ python -mtimeit -s\'import numpy as np\' \'
>     np.frombuffer(np.random.bytes(20000000), dtype=np.uint8)\'
10 loops, best of 3: 38.6 msec per loop
如果
numpy.dtype(\'u8\')
不是错字,而您确实需要
numpy.uint64
,则:
a = np.int64(np.random.random_integers(0, _BLOCK_MAX, blocks_per_flush))
in_memory_blocks = a.view(np.uint64) # unsigned
注意:如果数组的dtype已经是
np.int64
,则
np.int64()
不会复制。
.view(numpy.uint64)
强制其解释为未签名(也不执行任何复制)。     
如果您只是尝试一次填充一个文件,每个块大小为byte_size个字节,这可能比以前的答案要快。基于生成器并完全绕过数组创建:
import numpy as np

def random_block_generator(block_size):
    while True:
        yield np.random.bytes(block_size)

rbg = random_block_generator(BLOCK_SIZE)
那么您的用法是:
f = open(\'testfile.bin\',\'wb\')

for _ in xrange(blocks_to_write):
    f.write( rbg.next())

f.close()
Numpy使用确定性随机数生成(序列中的下一个数字始终相同,初始化时它只是在随机位置开始)。如果您需要真正的随机数据(加密级别),则可以使用
import Crypto.Random as cr
yield cr.get_random_bytes(block_size)
代替np。 另外,如果您的BLOCK_SIZE是已定义的常量,则可以使用这样的生成器表达式(这次使用Crypto库):
import Crypto.Random as cr
from itertools import repeat

BLOCK_SIZE = 1000

rbg = (cr.get_random_bytes(BLOCK_SIZE) for _ in repeat(0))

f = open(\'testfile.bin\',\'wb\')

for _ in xrange(blocks_to_write):
    f.write( rbg.next())

f.close()
其中包括实施“ 34”和执行。即使生成器方法的Crypto.Random稍微慢一点,它也会在使计算最大化之前很久就从磁盘i / o最大化(尽管我确定其他答案也是如此)。 更新: Athlon X2 245上的一些计时数据- 加密:生成500MB,不写-10.8s(46 MB / s) 加密:生成500MB并写入-11.2s(44.5 MB / s) numpy:生成500MB,不写入-1.4秒(360 MB /秒) numpy:产生500MB,然后写入-7.1s(70 MB / s) 因此,numpy版本的速度大约快8倍(足够快以使我的旧磁盘驱动器最大化)。我使用生成器表达式形式而不是生成器函数形式测试了它们两者。     
我不是很擅长优化,但是我看不到代码可以更快运行的方式。您正在使用纯迭代器和O(1)访问结构。 我认为问题是您选择的语言。请记住,您正在虚拟机中运行,并在其中运行解释器。您的C程序将始终以一个数量级的速度运行。     

要回复问题请先登录注册