All*_*rge 8 python optimization numpy
我正在尝试使用以下代码填充预分配的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)
Run Code Online (Sandbox Code Playgroud)
一些要点:
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.._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...')
# ...
Run Code Online (Sandbox Code Playgroud)
这种变体比@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
Run Code Online (Sandbox Code Playgroud)
如果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
Run Code Online (Sandbox Code Playgroud)
注意:np.int64()如果数组的dtype已经存在,则不会复制np.int64..view(numpy.uint64)强制将其解释为无符号(也不执行复制).
由于您要分配连续的块,因此您应该能够执行以下操作(完全摆脱内部循环):
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)
Run Code Online (Sandbox Code Playgroud)
这使用了numpy.random.randint分配整个内存块并用随机整数填充它的函数(请注意下面 JF Sebastian 关于numpy.random.randintvs 的评论random.randint)。没有办法(据我所知)使用 numpy 随机例程填充预分配的数组。另一个问题是 numpy 的 randint 返回 int64 数组。如果您需要其他大小的整数,则可以使用 numpy 类型方法,例如 numpy.uint8。如果您希望 randints 覆盖类型的整个范围,那么下面使用 numpy.random.bytes 的@JF Sebastian的方法将是最好的(几乎在任何情况下!)。
然而,简单的测试显示了合理的时间(与 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
Run Code Online (Sandbox Code Playgroud)
在我的 4 年旧 Core2 笔记本电脑上,我发现每次分配大约需要 0.7 秒(它运行了 50 次,因此运行整个测试需要更长的时间)。每次分配 20,000,000 个随机 uint8 整数需要 0.7 秒,因此我预计整个 500MB 大约需要 20 秒。
更多的内存意味着您可以一次分配更大的块,但是当您只需要 8 个 int 时,您仍然有效地浪费时间为每个 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)
Run Code Online (Sandbox Code Playgroud)
而不是:
in_memory_blocks[:blocks_per_flush] = numpy.random.randint(
0, _BLOCK_MAX+1, blocks_per_flush)
Run Code Online (Sandbox Code Playgroud)
然而,在计时之后,第一种情况不会导致速度显着增加(仅约 2%),因此可能不值得过于担心。我想绝大多数时间都花在实际生成随机数上(这是我所期望的)。
| 归档时间: |
|
| 查看次数: |
2501 次 |
| 最近记录: |