Поскольку вы размещаете смежные блоки, вы должны быть в состоянии сделать следующее (полностью избавиться от внутреннего цикла):
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
, которая выделяет целый блок памяти и заполняет его случайными целыми числами (см. Комментарий Дж. Ф. Себастьяна ниже о numpy.random.randint
против random.randint
). Насколько я могу видеть, нет никакого способа заполнить предварительно выделенный массив, используя множество случайных подпрограмм. Другая проблема заключается в том, что randint numpy возвращает массивы int64. Если вам нужны целые числа другого размера, то вы можете использовать методы набора номера, например numpy.uint8. Если вы хотите, чтобы randints охватывали весь диапазон типа, тогда @ J. Метод Ф. Себастьяна , представленный ниже с использованием numpy.random.bytes, будет наилучшим (почти в любом случае!).
Однако простые тесты показывают разумное время (того же порядка, что и код C). Следующий код проверяет время для выделения массивов uint8 из 20 000 000 случайных целых чисел с использованием метода numpy:
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 раз, так что весь тест займет больше времени) Это 0,7 с на 20 000 000 случайных целых чисел uint8, так что я бы ожидал что-то около 20 с для всех 500 МБ.
Больше памяти означало бы, что вы могли бы выделять большие порции сразу, но вы по-прежнему эффективно тратите время, выделяя и записывая 64 бита для каждого целого, когда вам нужно только 8 (я не определял этот эффект количественно). Если он все еще недостаточно быстр, вы можете вызвать реализацию C, используя интерфейс numpy ctypes. Это действительно довольно легко использовать, и вы не получите практически никакого замедления по сравнению с чистым C.
Общее сообщение о том, что при использовании numpy всегда старайтесь использовать numpy-подпрограммы там, где они существуют, помня, что возврат к C с помощью ctypes не слишком болезнен. В целом, эта методология позволяет действительно довольно эффективно использовать 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%), поэтому, вероятно, не стоит беспокоиться о слишком большом количестве. Я предполагаю, что подавляющее количество времени тратится на генерацию случайных чисел (что я и ожидал).