Комментарии 76
НЛО прилетело и опубликовало эту надпись здесь
Черно-белая подсветка кода выглядит классно. Еще бы оттенков серого добавить и был бы шедевр.
+2
Отформатируйте, пожалуйста, код моноширинным шрифтом
-2
НЛО прилетело и опубликовало эту надпись здесь
На самом деле нужно просто-напросто и всего-навсего примерно набросать области применения различных методов работы с сетью и решить для себя, в каком случае какой применять. Я по большому счету знаю три:
1) Классический многопроцессорный, я форком на каждое соединение.
+ Самый надежный и в принципе простой в написании
— Плохо подходит на Виндовс
— Нужно заморачиваться с IPC
2) Мультитредовый
+ Более производительный, чем 1)
— Менее надежный, чем 1)
+ Лучше работает на Виндовс
3) Асинхронный
+ Самый производительный
— надежность хуже чем 1)
+ работает и на Вин и на Лин (Unix)
Дисклаймер: Все вышесказанное — мое ИМХО, ни за что не отвечаю.
1) Классический многопроцессорный, я форком на каждое соединение.
+ Самый надежный и в принципе простой в написании
— Плохо подходит на Виндовс
— Нужно заморачиваться с IPC
2) Мультитредовый
+ Более производительный, чем 1)
— Менее надежный, чем 1)
+ Лучше работает на Виндовс
3) Асинхронный
+ Самый производительный
— надежность хуже чем 1)
+ работает и на Вин и на Лин (Unix)
Дисклаймер: Все вышесказанное — мое ИМХО, ни за что не отвечаю.
+1
третий вариант хоть и самый производительный, но на нём одном к сожалению не реализовать ничего сложного, видимо поэтому он у вас и получается самым производительным :)
а нам приходиться дополнительно городить свои планировщики и выбирать использование безстэковых/стэковых coroutine для простоты программирования. либо вырисовывая различные конечные автоматы, восстановление стэка в которых тоже бывает не такой уж скоростной операцией с кучей кэш миссов.
а нам приходиться дополнительно городить свои планировщики и выбирать использование безстэковых/стэковых coroutine для простоты программирования. либо вырисовывая различные конечные автоматы, восстановление стэка в которых тоже бывает не такой уж скоростной операцией с кучей кэш миссов.
0
> но на нём одном к сожалению не реализовать ничего сложного,
что вы имеете ввиду?
что вы имеете ввиду?
0
1) Классический многопроцессорный
стэйт хранится в стэке
2) Мультитредовый
стэйт хранится в стэке
3) Асинхронный
>а нам приходиться дополнительно городить…
стэйт хранится в стэке
2) Мультитредовый
стэйт хранится в стэке
3) Асинхронный
>а нам приходиться дополнительно городить…
+1
Вообще, необходимо расширить описание этих трех пунктов. Поправьте, если я ошибаюсь
Примеры прилжожений:
1) Apache по классической схеме (не мультитредовый) — Известен своей надежностью
2) IIS, Apache версии 2.0, мультитредовый. Пока не рекомендуется там, где нужна высокая надежность
3) Nginx — отличается самой высокой производительностью
Рекомендации к выбору решения:
1) Если нужно быстро сделать приложение, которое будет работать достаточно надежно, ДАЖЕ при присутствии в коде немалого количества ошибок. Трудозатраты низкие.
2) Если нужно сделать преносимое Unix/Win32 решение. Трудозатраты на разработку низкие, на отладку и доведение до уровня надежности 1) высокие
3) Если нужна максимальная производительность. Самые высокие трудозатраты.
Примеры прилжожений:
1) Apache по классической схеме (не мультитредовый) — Известен своей надежностью
2) IIS, Apache версии 2.0, мультитредовый. Пока не рекомендуется там, где нужна высокая надежность
3) Nginx — отличается самой высокой производительностью
Рекомендации к выбору решения:
1) Если нужно быстро сделать приложение, которое будет работать достаточно надежно, ДАЖЕ при присутствии в коде немалого количества ошибок. Трудозатраты низкие.
2) Если нужно сделать преносимое Unix/Win32 решение. Трудозатраты на разработку низкие, на отладку и доведение до уровня надежности 1) высокие
3) Если нужна максимальная производительность. Самые высокие трудозатраты.
+1
1 и 2) легко расширяются с помощью модулей, можно делать блокирующие вызовы, какую-нибудь тяжёлую работу, зависимость в модулях от проприетарщины, которая делает блокирующие вызовы.
3) нужно подстраиваться под стэйт машину, которая была реализована в этом приложении. Не знаю как сейчас, но раньше с nginx'ом небыло возможности даже сделать простое синхронное логирование без блокирующего вызова в своём модуле. Но это не проблема данного подхода, просто так уж сделали.
3) нужно подстраиваться под стэйт машину, которая была реализована в этом приложении. Не знаю как сейчас, но раньше с nginx'ом небыло возможности даже сделать простое синхронное логирование без блокирующего вызова в своём модуле. Но это не проблема данного подхода, просто так уж сделали.
+1
Реальная многопоточность несет оверхед, больше переключений контекста, например (кстати, почему разработчики процессоров не сделают эту операцию быстрой, рах ее постоянно упоминают как причинц снижения производительности?). Плюс нужна синхронизация для доступа к общим переменным, а это опять-так и ломает всю производительность.
И вообще, кто вам сказал, что многопоточность — хорошо? Как вообще может быть хорошей программа, в которой несколько потоков работают в общей области памяти и любой может вызвать трудно обнаружимую ошибку?
И вообще, кто вам сказал, что многопоточность — хорошо? Как вообще может быть хорошей программа, в которой несколько потоков работают в общей области памяти и любой может вызвать трудно обнаружимую ошибку?
0
>больше переключений контекста
А это уж как реализовано в ОС.
>Как вообще может быть хорошей программа, в которой несколько потоков работают в общей области памяти
Если сделать доступ атомарным, то программа будет чрезвычайно хорошей.
Вобщем, ничего плохого в потоках нет, надо просто уметь их готовить.
А это уж как реализовано в ОС.
>Как вообще может быть хорошей программа, в которой несколько потоков работают в общей области памяти
Если сделать доступ атомарным, то программа будет чрезвычайно хорошей.
Вобщем, ничего плохого в потоках нет, надо просто уметь их готовить.
0
> Как вообще может быть хорошей программа,
> в которой несколько потоков работают в общей области памяти,
> и любой может вызвать трудно обнаружимую ошибку?
Есть хороший способ избежать этой проблемы — отказаться от общих переменных.
> в которой несколько потоков работают в общей области памяти,
> и любой может вызвать трудно обнаружимую ошибку?
Есть хороший способ избежать этой проблемы — отказаться от общих переменных.
0
В статье не раскрыт вопрос почему асинхронность (и данное решение в частности) лучше многопоточности.
0
Ест меньше ресурсов. Отлично было раскрыто в одной из статей Ивана Сагалаева, про то как он писал некий кусок кода для скачивания медиа-файликов, и в итоге пришел к asyncore. Найдите, прочтите, просветление настигнет внезапно.
+2
А, почему не раскрыто. Автор видимо не подумал, что это надо раскрывать в сотый раз — на хабре есть уже множество статей, как однопоточное приложение может обойти многопоточное.
0
А смысл раскрывать очевидный вопрос?
Асинхронность ортогональна многопоточности, как их сравнивать вообще?
Асинхронность ортогональна многопоточности, как их сравнивать вообще?
0
Неблокирующую работу с сетью можно использовать вместе с многопоточностью, где кол-во потоков сделать равным кол-ву логических процессоров и все будут довольны :)
0
А может все же проще плюнуть на сервер пачку запросов аяксом?
Эффект тот же
:-)
Эффект тот же
:-)
-7
«Кроме того, в Питоне все равно не существует настоящих потоков уровня ядра, а здравствует и по сей день треклятый GIL. Соответственно, никаких преимуществ в производительности на многоядерных процессорах получить нельзя.»
Можно пояснить? Я сейчас написал сприптик на питоне — создаю 4 потока в каждом деалаю 1000*1000, вижу в топ 4 потока отъдающие по ~100% cpu и еще один ~0,01 (я полагаю GIL). Те вроде как получается занять все 4 ядра. python25 если это важно.
Можно пояснить? Я сейчас написал сприптик на питоне — создаю 4 потока в каждом деалаю 1000*1000, вижу в топ 4 потока отъдающие по ~100% cpu и еще один ~0,01 (я полагаю GIL). Те вроде как получается занять все 4 ядра. python25 если это важно.
0
угу. А попробуйте сначала сделать 100 тыс раз (1000*1000) тупо в одном потоке, а потом по 25 тыс. раз в 4 четырех параллельных и сравните время )
0
4 потока по 2500000 итрераций умножения
real 0m3.319s
user 0m3.299s
sys 0m0.018s
без потоков 10000000 итераций
real 0m4.704s
user 0m4.758s
sys 0m0.310s
Выйрыша небольшой, но 1) он есть 2) потоки native 3) на других задачах разница может быть выше. Натив threads в питон в версии 2.4 уже точно были. Журнал Dr. Dobb's в свое время писал на эту тему.
real 0m3.319s
user 0m3.299s
sys 0m0.018s
без потоков 10000000 итераций
real 0m4.704s
user 0m4.758s
sys 0m0.310s
Выйрыша небольшой, но 1) он есть 2) потоки native 3) на других задачах разница может быть выше. Натив threads в питон в версии 2.4 уже точно были. Журнал Dr. Dobb's в свое время писал на эту тему.
+1
Видимо я просто не понимаю как это все работает. Ну вот смотрите например gil картинках.
www.dabeaz.com/blog/2010/01/python-gil-visualized.html
www.dabeaz.com/blog/2010/01/python-gil-visualized.html
0
По ссылке все написано верно, но там пример 2 потока I/O и CPU интенсивыный, CPU интенсивный и дергает постоянно GIL, потому что только в нем и интрпретируется python код, в то время как I/O вызовы умеют освобождать GIL. Те в случае двух I/0 процессов — картина была бы совсем иной.
0
ммм. А где там написано вообще что-то про потоки IO? Там мне кажется как раз ваш случай — 2 CPU потока без IO которые переодически переключаются между собой по тикеру.
0
In this graph, you're seeing how difficult it is for the I/O bound to get the GIL in order to perform its small amount of processing.
Там совершенно не мой случай, мой случай 4 CPU-bound thread, а по ссылке 1CPU-bound а втрой merely echoes UDP packets.
Там совершенно не мой случай, мой случай 4 CPU-bound thread, а по ссылке 1CPU-bound а втрой merely echoes UDP packets.
0
Если вы делаете просто:
for i in range(2500000):
1000 * 1000
то интерпретатор оптимизирует это выражение и умножения просто не будет при каждой итерации.
добавьте: * i
тогда увидите реальную картину при которой вариант с тредами медленнее.
for i in range(2500000):
1000 * 1000
то интерпретатор оптимизирует это выражение и умножения просто не будет при каждой итерации.
добавьте: * i
тогда увидите реальную картину при которой вариант с тредами медленнее.
0
Я как раз это понимаю, результаты выше для такого кода
hcount = 2500000
while (hcount>1):
j=1000*1000
hcount=hcount-1
4 потока с таким циклом
hcount = 2500000
while (hcount>1):
j=1000*1000
hcount=hcount-1
4 потока с таким циклом
0
Вот как раз тут у вас нет умножения:-)
Лучше вообще взять классический пример с счетчиком:
COUNT = 100000000
CPU_COUNT = 4
def count(n):
while n:
n -= 1
def sequence():
for i in range(CPU_COUNT):
count(COUNT)
def parallel():
pool = []
for i in range(CPU_COUNT):
t = threading.Thread(target=count, args=(COUNT,))
t.start()
pool += [t]
[t.join() for t in pool]
Лучше вообще взять классический пример с счетчиком:
COUNT = 100000000
CPU_COUNT = 4
def count(n):
while n:
n -= 1
def sequence():
for i in range(CPU_COUNT):
count(COUNT)
def parallel():
pool = []
for i in range(CPU_COUNT):
t = threading.Thread(target=count, args=(COUNT,))
t.start()
pool += [t]
[t.join() for t in pool]
0
Скорее да — но вот ведь прикол — пустой цикл в 4 потока отработал чуть быстрее чем в 1.
0
Трудно анализировать такое поведения, не видя код.
0
#!/usr/bin/python3.1
import threading
class mythread(threading.Thread): # subclass Thread object
def __init__(self, myId, hc ):
self.myId = myId
self.hcount = hc
threading.Thread.__init__(self)
def run(self): # run provides thread logic
hcount = 2500000
while (hcount>1):
j=1000*1000
hcount=hcount-1
MAXTHR=4
threads = []
for t in range(MAXTHR):
thread = mythread(t, 0) # make/start 10 threads
thread.start( ) # start run method in a thread
threads.append(thread)
for thread in threads:
thread.join( ) # wait for thread exits
Вот такой вот запускал
import threading
class mythread(threading.Thread): # subclass Thread object
def __init__(self, myId, hc ):
self.myId = myId
self.hcount = hc
threading.Thread.__init__(self)
def run(self): # run provides thread logic
hcount = 2500000
while (hcount>1):
j=1000*1000
hcount=hcount-1
MAXTHR=4
threads = []
for t in range(MAXTHR):
thread = mythread(t, 0) # make/start 10 threads
thread.start( ) # start run method in a thread
threads.append(thread)
for thread in threads:
thread.join( ) # wait for thread exits
Вот такой вот запускал
0
А говорили что питон 2.5:-)
Слишком мало повторений. На таких объемах накладные расходы на переключение GIL не так отчетливо проявляются. А полученный временной результат может зависеть от общей нагруженности системы в какой-то конкретный момент запуска теста.
Суть в том, что треды как минимум не быстрее в CPU-bound задачах.
Слишком мало повторений. На таких объемах накладные расходы на переключение GIL не так отчетливо проявляются. А полученный временной результат может зависеть от общей нагруженности системы в какой-то конкретный момент запуска теста.
Суть в том, что треды как минимум не быстрее в CPU-bound задачах.
0
Кстати, судя по всему, в питоне 3.2 сделают быстрый GIL. Он останется, но не будет так тупить на счетных задачах. Раньше переключение шло каждые N инструкций (что, понятное дело, убивало все к черту на счетных циклах), теперь будет идти каждые N миллисекунд.
www.dabeaz.com/python/NewGIL.pdf
www.dabeaz.com/python/NewGIL.pdf
0
> что, понятное дело, убивало все к черту на счетных циклах
Убивало не это, а то что тред, отпустив GIL, мог сам же его снова захватить. Так было из-за того, что он не знает есть ли кто-то другой ждущий выполнения, а OS в свою очередь не успевала в этот интервал переключить контекст.
В новом варианте он отпускает его сам только если флаг выставлен и потом засыпает до того момента как GIL захватит другой тред.
То что всю эту системы перевели на временные интервалы в место числа инструкций, можно сказать, побочный эффект нового подхода:-)
Убивало не это, а то что тред, отпустив GIL, мог сам же его снова захватить. Так было из-за того, что он не знает есть ли кто-то другой ждущий выполнения, а OS в свою очередь не успевала в этот интервал переключить контекст.
В новом варианте он отпускает его сам только если флаг выставлен и потом засыпает до того момента как GIL захватит другой тред.
То что всю эту системы перевели на временные интервалы в место числа инструкций, можно сказать, побочный эффект нового подхода:-)
0
Спецом по GIL не являюсь, код питона не читал, сужу по разным записям в интернете. Берем первоисточник:
mail.python.org/pipermail/python-dev/2009-October/093321.html
Автор патча пишет 3 вещи, которые его в GIL не устраивали, снижали производительность и побудили, в конце концов, написать патч. Первые 2 причины — это то, что я написал, 3я — то, что ты.
mail.python.org/pipermail/python-dev/2009-October/093321.html
Автор патча пишет 3 вещи, которые его в GIL не устраивали, снижали производительность и побудили, в конце концов, написать патч. Первые 2 причины — это то, что я написал, 3я — то, что ты.
0
НЛО прилетело и опубликовало эту надпись здесь
что за жизнь без multiprocessing? (на худой конец хоть multi-threading)
в CPython dev team как клала на это болт так и кладёт.
Хорошо хоть мульти-ядерники стали чаще появляться на столах. Команда CPython стала немножко шевелиться в эту сторону.
вся надежда на unladen swallow, но и им не легко.
в CPython dev team как клала на это болт так и кладёт.
Хорошо хоть мульти-ядерники стали чаще появляться на столах. Команда CPython стала немножко шевелиться в эту сторону.
вся надежда на unladen swallow, но и им не легко.
-2
кст, проблема в том, что в CPython нет и настоящих процессов…
-1
Т.е.? Не писал, врать не буду, но вроде ведь можно форк вызвать.
0
можно и просто multiprocessing module пользовать.
но GIL и ref counting убивают любое не слишком тривиальное приложение даже сидящее на мультипроцессинге.
но GIL и ref counting убивают любое не слишком тривиальное приложение даже сидящее на мультипроцессинге.
0
По идее у каждого процесса будет свой GIL и refcounting.
0
верно. а проблема таки вылазит:
если есть обычная жирная структура в памяти, к которой процессы лазят даже лишь на чтение, то каждый процесс из-за этих ref counters получает себе copy-on-write копию этого дурацкого реф-каунтера со всей его жирной окрестностью в RAM.
вот и получается: там где вы в С можете делать 100 процессов без проблем, там же в CPython вы имеете 100 кратное увеличение ерундового 500Мб куска в памяти.
если есть обычная жирная структура в памяти, к которой процессы лазят даже лишь на чтение, то каждый процесс из-за этих ref counters получает себе copy-on-write копию этого дурацкого реф-каунтера со всей его жирной окрестностью в RAM.
вот и получается: там где вы в С можете делать 100 процессов без проблем, там же в CPython вы имеете 100 кратное увеличение ерундового 500Мб куска в памяти.
+1
s/увеличение/размножение
0
man execve
execve() does not return on success, and the text, data, bss, and stack
of the calling process are overwritten by that of the program loaded.
Никакого увеличения куска памяти. Используйте инструменты с умом.
execve() does not return on success, and the text, data, bss, and stack
of the calling process are overwritten by that of the program loaded.
Никакого увеличения куска памяти. Используйте инструменты с умом.
0
мы вроде о CPython тут?
0
а теперь раскройте вашу мысль применительно к этому конкретному короткому примеру, пожалуйста:
habrahabr.ru/blogs/python/81716/#comment_2422547
habrahabr.ru/blogs/python/81716/#comment_2422547
0
в контретно том примере используется только fork, из-за которого получаем всё наследие родителя на которое вы жалуетесь.
0
> Используйте инструменты с умом.
> [...] snip [...]
ни на что я не жалуюсь.
> [...] snip [...]
ни на что я не жалуюсь.
0
Тут возможно недопонимание COW? Нам нужно наследство родителя, мы хотим его, мы хотим им пользоваться. но без лишних затрат. Это дает нам fork() и любим мы его именно за это.
В классическом случае мы получаем наследие родителя полностью, но если мы его только читаем, то нм получение наследия никаких затрат мы не несем.
Но в примере vak мы казалось бы получили в дочерние процессы данные, которые только читаем, по идее у каждого дочернего процесс должна быть большая виртуальная память и маленькая резидентная. На Си или Спп так бы и было, однако на Pythone получается не так из-за указанных vak особенностей реализации сборщика мусора.
Это ни плохо ни хорошо, это реальность данная нам в ощущениях.
А вот если от этого эффекта удасться уйти без больших затрат, это вообще будет суперкул!
В классическом случае мы получаем наследие родителя полностью, но если мы его только читаем, то нм получение наследия никаких затрат мы не несем.
Но в примере vak мы казалось бы получили в дочерние процессы данные, которые только читаем, по идее у каждого дочернего процесс должна быть большая виртуальная память и маленькая резидентная. На Си или Спп так бы и было, однако на Pythone получается не так из-за указанных vak особенностей реализации сборщика мусора.
Это ни плохо ни хорошо, это реальность данная нам в ощущениях.
А вот если от этого эффекта удасться уйти без больших затрат, это вообще будет суперкул!
+1
100% ditto.
кст, есть неплохая заплатка от итальянца, который хотя бы сдвигает эти реф каунтеры в одно место. пару недель назад он собирался «дополировать» его (я пока не смотрел чем дело кончилось).
кст, есть неплохая заплатка от итальянца, который хотя бы сдвигает эти реф каунтеры в одно место. пару недель назад он собирался «дополировать» его (я пока не смотрел чем дело кончилось).
0
Так нужно чтобы задача выполнялась быстрее в CPython'е или наследство родителя? На Си вообще большинство вещей делалось бы иначе. В питоне свои особенности, зачем пытаться делать так же как в си…
Можно попробовать через пайп прокидывать всё нужное наследство, так получим параллелизм(если конечно кол-во логических процессоров больше одного)
ещё можно приклеить clone или posix_spawn к питону и делать clone/exec без оверхеда на копирование таблиц для работы cow.
Можно попробовать через пайп прокидывать всё нужное наследство, так получим параллелизм(если конечно кол-во логических процессоров больше одного)
ещё можно приклеить clone или posix_spawn к питону и делать clone/exec без оверхеда на копирование таблиц для работы cow.
0
Пока процессы не пишут в этот кусок, они пользуются физически одной и той же памятью, так что увеличение использованной памяти в многопроцессорной системе не происходит. (Называется этот механизм как раз copy-on-write, суть в том, что страница памяти, если не обшибаюсь, как правило 4k, физически выделяется дочернему процессу тогда и только в тот момент, когда он попробует в нее записать)
0
Да, это все, что я написал, это Linux и fork(), в Виндовсе все не так гладко.
0
именно.
и, казалось бы, всё шоколадно и CPython должен бы профитировать от этого механизма тоже?
беда в том, как я попытался уже написать выше, что ref counters изменяются даже при операциях чтения. а это провоцирует copy-on-write копирование.
последний гвоздь — ref counters не живут в одномлепрозории месте в памяти,
а лежат в заголовке структуры, которую они обслуживают.
результат? — несложно догадаться.
нужен 10 строчный пример кода? дайте знать.
P.S. и, как обычно, те, кто не в теме, умеют, однако, жать на минус вполне уверенно. ну, ничего, меньше возможностей писать, больше времени на что-то полезное :)
и, казалось бы, всё шоколадно и CPython должен бы профитировать от этого механизма тоже?
беда в том, как я попытался уже написать выше, что ref counters изменяются даже при операциях чтения. а это провоцирует copy-on-write копирование.
последний гвоздь — ref counters не живут в одном
а лежат в заголовке структуры, которую они обслуживают.
результат? — несложно догадаться.
нужен 10 строчный пример кода? дайте знать.
P.S. и, как обычно, те, кто не в теме, умеют, однако, жать на минус вполне уверенно. ну, ничего, меньше возможностей писать, больше времени на что-то полезное :)
+1
Несколько отвлеченное соображение. Я в Питоне недавно, где-то 1-2+ года. Для меня вообще стало открытием, что универсальный скриптовый язык используется для написания неплохо работающих, в том числе и по производительности серверов, в том числе и многопоточных. Это приятная неожиданность. Надеюсь, что и проблему с GIL решат. Вот тогда и наступит коммунизм!
0
> и, казалось бы, всё шоколадно и CPython должен бы профитировать от этого механизма тоже?
Но ведь, ИМХО, должен. Ведь для разных процессов и GIL-ы разные и проэтому процессы могут исполняться на разных ядрах без простоев. Единственное, что экономия памяти и времени будет меньше из-за выделения страниц, в которые подпадают ref-counters.
> последний гвоздь — ref counters не живут в одном лепрозории месте в памяти,
а лежат в заголовке структуры, которую они обслуживают.
результат? — несложно догадаться.
> нужен 10 строчный пример кода? дайте знать.
Давайте. Не для спора, а просто интересно для углубления в проблему.
Но ведь, ИМХО, должен. Ведь для разных процессов и GIL-ы разные и проэтому процессы могут исполняться на разных ядрах без простоев. Единственное, что экономия памяти и времени будет меньше из-за выделения страниц, в которые подпадают ref-counters.
> последний гвоздь — ref counters не живут в одном лепрозории месте в памяти,
а лежат в заголовке структуры, которую они обслуживают.
результат? — несложно догадаться.
> нужен 10 строчный пример кода? дайте знать.
Давайте. Не для спора, а просто интересно для углубления в проблему.
0
import time from multiprocessing import Pool def f(_): time.sleep(5) res = sum(parent_x) # "read-only" return res if __name__ == '__main__': parent_x = [1./i for i in xrange(1,10000000)]# read-only data p = Pool(7) res= list(p.map(f, xrange(10))) print res
заходите в ps/top или в любой другой ваш любимой монитор памяти и смотрите как процессы взрывают mem usage.
+2
Спасибо, проверил, все действительно так, будем учитывать эту фичу. Поигрался немного с текстом программы, прогнал три варианта:
1) Ваш вариант
2) То же, на форке (переписал для себя, для понятности)
3) На форке, с вычислением суммы внутри функции
res = sum(1./i for i in xrange(1,10000000))
Результаты получились ожидаемые, большой точности не добивался, но все-таки:
Выводы:
1) Описанный Вами эффект оказывает сильное вредное влияние при больших объемах часто используемых данных
2) Разница во времени между 1) и 2) — скорее всего погрешности эксперимента
3) При вычислениях по функциональному типу даже вычисляя одно и то же отдельно в каждом процессе проигрыш по времени неожиданно мал. Объяснить — не хватает моих знаний внутренностей Python
4) Вычисления по функциональному типу дают большую экономию памяти.
В принципе, все это давно известно, но интересно было потрогать своими руками.
Еще раз спасибо.
1) Ваш вариант
2) То же, на форке (переписал для себя, для понятности)
3) На форке, с вычислением суммы внутри функции
res = sum(1./i for i in xrange(1,10000000))
Результаты получились ожидаемые, большой точности не добивался, но все-таки:
Вариант Время ПамВирт ПамРез ПамШар 1) 14с 200 198 836 2) 11с 198 193 432 3) 17с 5456 1780 460
Выводы:
1) Описанный Вами эффект оказывает сильное вредное влияние при больших объемах часто используемых данных
2) Разница во времени между 1) и 2) — скорее всего погрешности эксперимента
3) При вычислениях по функциональному типу даже вычисляя одно и то же отдельно в каждом процессе проигрыш по времени неожиданно мал. Объяснить — не хватает моих знаний внутренностей Python
4) Вычисления по функциональному типу дают большую экономию памяти.
В принципе, все это давно известно, но интересно было потрогать своими руками.
Еще раз спасибо.
0
gern geschehen!
надеюсь, команда unladen swallow не сдастся и добьёт эту важную тему до конца.
З.Ы. хотя мне самому осталось не очень ясно зачем я тут минусы собираю. пополз-ка я обратно, в читателей ;)
надеюсь, команда unladen swallow не сдастся и добьёт эту важную тему до конца.
З.Ы. хотя мне самому осталось не очень ясно зачем я тут минусы собираю. пополз-ка я обратно, в читателей ;)
+2
Пардон, заметил недочет. Таблицу следует читать так:
Вариант Время ПамВирт ПамРез ПамШар 1) 14с 200M 198M 836 2) 11с 198M 193M 432 3) 17с 5456 1780 460
0
хм, 1780 без «М»?.. если ошибки нет, то такое впечатление, что форковые версии процессов практически не пересекались во времени исполнения, но это маловероятно…
З.Ы. и, кст, спасибо за :)
З.Ы. и, кст, спасибо за :)
0
Вот код:
#! /usr/bin/python # http://habrahabr.ru/blogs/python/81716/ import time,os,sys def f(): time.sleep(5) res = sum(1./i for i in xrange(1,10000000)) return res if __name__ == '__main__': t0 = time.time() for i in range(7): pid = os.fork() if(pid == 0): res = f() print res sys.exit(0) else: next os.wait() print "time =", time.time() - t0
0
напомнило мои извращения с php, только функции слегка другие, а суть таже, см php.net/socket_select
+1
>[b]параллельные[/b] соединения c http-cервером
Parallel != Concurrent
en.wikipedia.org/wiki/Parallel_computing
en.wikipedia.org/wiki/Concurrent_computing
Parallel != Concurrent
en.wikipedia.org/wiki/Parallel_computing
en.wikipedia.org/wiki/Concurrent_computing
0
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
Асинхронный http-клиент, или почему многопоточность — лишнее