Писал уже раньше о маленьком скрипте, который предназначался для патчинга группы файлов. Писал я о нем с точки зрения производительности и в комментах был озвучен вопрос -- а есть ли смысл загрузку файлов сделать асинхронной (т.е. фоновой)?
Сам скрипт в цикле бежит по списку файлов, и в одной итерации делает две "тяжелые" вещи. Первое -- он читает целый файл с диска в память. Второе -- ищет в нем заданные строки. Понятно, что первая операция это чистое I/O, практически не потребляющее CPU, а значит можно получить profit даже на одноядерном камне, запустив ее в фоне в упреждающем режиме (т.е. пока ищем в текущем файле, читаем следующий файл с диска).
Как известно, с многопоточностью в питоне дела обстоят очень грустно. В рамках одного экземпляра интерпретатора в один момент времени может исполнятся только один поток. Потому что GIL. Потому что вот так вот плохо в свое время это все было придумано.
В нашем случае есть зацепка -- документация утверждает, что для I/O операций GIL временно снимается (что логично). Для проверки этого факта я набросал небольшой тестовый скриптик.
Copy Source | Copy HTML
def ThreadTest():import threading, time CFile = r
"..." # your huge file on HDD
CBlockSize = 512 * 1024 # 32 * 1024 * 1024def SingleAction():
sum = 0
for i in range(2 * 1024):
sum += i
return sumdef Benchmark(durationSec):
t = time.clock()
i = 0
while time.clock() - t < durationSec:
SingleAction()
i += 1
t = time.clock() - t
return i / tdef LoadFile():
nonlocal readDone size =
0
data = bytearray(CBlockSize * b'\x00')
with io.open(CFile, 'br') as f:
while True:
lx = f.readinto(data)
size += lx
if lx != len(data): breakprint("Size", size)
readDone = True# bench
print('Start...')
print(Benchmark(1. 0)) # ~2'100# thread test
readDone = False t = time.clock()
thread = threading.Thread(target =
LoadFile)
thread.start() i =
0
while not readDone:
SingleAction()
i += 1
time.sleep( 0)if i == 0: i = 1 t = time.clock() - t
print('Time ', t, '; count', i, '; rate ', i / t)Вначале он проводит опорный бенчмарк в ситуации, когда процессор больше ничем другим не занимается. Потом -- запускает этот же бенчмарк параллельно с тредом, который читает с диска файл большого размера. Эксперимент заканчивается по факту прочтения файла, мы смотрим на то, как меняется результат бенчмарка, в случае, если в фоне идет чтение файла.
У меня на машине фоновая I/O нагрузка на результат бенчмарка практически не влияет (потери в районе 5%), из этого я сделал вывод, что есть смысл поиграться с упреждающим чтением файлов.
Что из этого получилось -- в следующей части.
Вся правда о GIL -- http://cd-riper.livejournal.com/224538.html
О производительности -- http://cd-riper.livejournal.com/236187.html
Детские мозаики - игрушки для детей. Игрушки для детей (Москва).