В мире современных операционных систем и процессов есть несколько способов передавать информацию между процессами:
- разделяемая память (shared memory),
- memory mapped file,
- pipe
- сокеты
- файловая система
Рассмотрим каждый из них по отдельности:
Разделяемая память (или shared memory) — это самый быстрый в плане производительности способ взаимодействия. Смысл его заключается в том, что необходимые данные размещаются в так называемой «общей памяти», к которой может обращаться любой процесс в операционной системе.
Memory mapped file — можно сказать, что это обычный файл с данными, но не на жестком диске, а в памяти ОЗУ. И так как отклик ОЗУ в разы выше отклика жесткого диска, то доступ к этим данным происходит намного быстрее. К минусу можно отнести то, что по дефолту единовременно обработку файла может делать только один процесс. По скорости этот способ совсем немного уступает предыдущему и является одним из самых быстрых
Pipe — каналы (потоки, трубы) информации. Несколько похож на потоковую работу Stream с файловой системой. Минус так же в том, что pipe может передавать информацию только между двумя процессами. Уступает по производительности первым двум.
Сокеты — взаимодействие через сетевое подключение. Тоже довольно быстрое. На низком уровне оно работает на скорости каналов. Но на высоких реализациях вся работа укладывается в протоколы и, как правило, в tcp-ip — это несколько замедляет взаимодействие вплоть до скорости работы с файловой системой.
Файловая система — работает через CreateFile API (Windows) довольно быстро и для некоторых конфигураций достигает скорости сокетов на tcp-ip. Из минусов можно отнести то, что одновременно (по дефолту) обработку может вести только один процесс, а мониторинг (состояния изменения файлов) более дорогой, т.к. требует постоянное взаимодействие с жестким диском и большую нагрузку на ЦП. Такой подход может привести к быстрому износу накопителя. Поэтому не рекомендуется.
Итак, к практике. Как то я столкнулся с задачей, в которой программе на python необходимо взаимодействовать с другим приложением: получать и отдавать данные. При чем эта «другая» программа — не питоническая. Решение нужно было быстрое в плане реализации и работы. 1й пункт отпал сразу, тк в пайтон разделяемая память в multiprocessing завязана только на питонические процессы. Memory Mapped File отпал так же.
Попытки реализовать через PIPE оказались неподходящими, поскольку их питоническая реализация довольно медленная и ограниченная (хотя вполне подходящая):
p = Popen(["cmd"], stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False) out, err = p.communicate( 'ping 192.168.1.1\n' ) print out.decode('cp866')
Ограничение состоит в том, что в питонической реализации PIPE нельзя одновременно читать и писать в канал. Т.к. канал один и тот же. В связи с этим, для того, чтобы прочитать ответ из канала после записи данных, нужно закрыть канал на запись. А открыть его уже не представляется возможным.
Следующим подходом была реализация через сокеты. Эта реализация оказалась достаточно быстрой (15 мс на запись и чтение на моем Intel Atom).
Примерно такая же скорость вышла и при чтении/записи файловой системе (в пайтон это работает довольно быстро). Но при мониторинге файловой системы без таймаута время передачи оказалось на уровне сокетов, но нагрузка на процессор увеличилась до 40%, а с минимальной задержкой 1-10 мс отклик по мониторингу оказался около 9 мс (+ время на запись/чтение передаваемых данных — в пайтоне — еще 6-7 мс, + чтение и такой же мониторинг в клиентском приложении — еще 12мс, итого примерно 27 мс) — такой подход оказывается дольше, чем на сокетах примерно на 70-80% + он накладывает лишнюю регулярную нагрузку на жесткий диск (в тч со стороны ожидающего приложения), а это не есть хорошо — пускай 500 циклов чтения/записи за 2-3 часа работы.
Выводы: работа через сокеты в целом на небольшую долю быстрее и немного менее требовательна, чем взаимодействие с файловой системой, но разница не глубока. Поэтому при выборе, если есть возможность реализовать через сокеты, то лучше выбрать их.