我正在尝试使用以下代码填充预分配的bytearray:
# 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
是hashilib.md5()
使用上面的代码生成1MB需要〜1s; 500MB需要~376s。相比之下,我使用rand()的简单C程序可以在8s内创建一个500MB的文件。
如何在上述循环中提高性能?我很确定我忽略了一些显而易见的事情,这会导致运行时间的巨大差异。
答案 0 :(得分:7)
由于0.._BLOCK_MAX
涵盖numpy.uint8
的所有可能值(我假设numpy.dtype('u8')
(即numpy.uint64
是拼写错误),您可以使用:< / p>
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's one快〜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)
强制将其解释为无符号(也不执行复制)。
答案 1 :(得分:4)
由于您正在分配连续的块,您应该能够执行以下操作(完全摆脱内部循环):
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)
这使用numpy.random.randint
函数,该函数分配整个内存块并用随机整数填充(注意J.F.Sebastian关于numpy.random.randint
与random.randint
的评论。使用numpy随机例程填充预分配的数组是没有办法(据我所知)。另一个问题是numpy的randint返回int64数组。如果你需要一些其他大小的整数,那么你可以使用numpy类型方法,例如numpy.uint8。如果你想要randints覆盖整个类型的范围,那么下面使用numpy.random.bytes的@J. F. Sebastian方法将是最好的(几乎在任何情况下都是如此!)。
然而,简单测试显示合理的时间(与C代码的数量级相同)。以下代码使用numpy方法测试分配20,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秒,所以我希望整个500MB大约20秒左右。
更多内存意味着您可以同时分配更大的块,但是当您只需要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%),因此可能不值得担心太多。我想绝大部分时间花在实际生成随机数上(这是我所期望的)。
答案 2 :(得分:0)
如果您只是尝试填充文件,一次阻塞块数字,这可能比以前的答案更快。基于生成器并完全绕过阵列创建:
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()
包括实施rbg=...
和执行。这种生成器方法,即使使用稍慢的Crypto.Random,也会在最大化计算之前从磁盘i / o中最大化(尽管我确定其他答案也是如此)。
更新:
Athlon X2 245上的一些时序数据 -
因此numpy版本的速度提高了约8倍(很快就足以使我的旧式拼盘驱动器最大化)。我使用生成器表达式而不是生成器函数形式测试了它们。
答案 3 :(得分:-1)
我不擅长优化,但我没有看到你的代码可以更快地运行的方式。您使用的是纯粹的迭代器和O(1)访问结构。
我认为这个问题是你选择的语言。请记住,您正在虚拟机中运行,并在此处使用解释器。你的C程序总是会快一个数量级。