LINUX.ORG.RU

Реализация PyPy языка Python избавляется от глобальных блокировок

 , ,


0

1

Глобальные блокировки в CPython (стандартная реализация языка python) долгое время были камнем преткновения и предметом многочисленных споров. В реализации PyPy, до недавнего времени, была применена схожая техника разграничения доступа к общим данным.

В настоящее время команда разработчиков проекта PyPy работает над альтернативным алгоритмом разграничения доступа к общим данным. Вместо блокировок предлагается использовать транзакции по аналогии с базами данных.

Вместо прямого изменения содержимого оперативной памяти программа работает со специальной областью (транзакционной памятью), в которую вносятся все изменения. По завершению обработки эти изменения атомарно вносятся в память программы. В случае обнаружения коллизии с другими транзакциями, изменения вносятся только от первой транзакции; остальные транзакции отменяются и отправляются на повторное выполнение.

Это оптимистичный алгоритм, каждая нить выполняют код без учёта того что делают другие нити.

Несмотря на то, что алгоритм может давать значительные потери производительности при высокой конкуренции за данные, он выглядит очень перспективным. Во-первых, он позволяет работать нескольким нитям независимо над разными частями одного объекта в памяти (при использовании блокировок пришлось бы ставить блокировку на весь объект целиком). Во-вторых, при возникновении многочисленных коллизий при доступе к одному объекту, менеджер транзакций может упорядочить выполнение кода (это будет аналогично наличию блокировки на этот объект).

В целом, хотя новый подход и имеет недостатки, он позволяет радикально улучшить производительность многих устоявшихся шаблонов проектирования. Например, в ситуации, когда каждая нить работает только со своими данными. В частности, это позволит эффективно масштабировать на многопроцессорных системах большинство серверных приложений.

>>> Подробности

★★★★★

Проверено: maxcom ()
Последнее исправление: maxcom (всего исправлений: 2)

Ответ на: комментарий от tensai_cirno

> Не стоит увлекаться адаптацией, а то PyPy в ПиПи превратиться таким образом может ;)

Как будто при взгляде на заголовок кто-то это по-другому читает :)

hobbit ★★★★★
()

я всё правильно понимаю: нет чтобы сделать классические механизмы(семафоры те же) и отправлять потоки в дешёвое ожидание, разработчики PyPy решили что лучше уж заставлять поток молотить цифры несколько раз. Я сейчас про худший случай, то есть блок кода, который пишет в разделяемую структуру данных, запущен в N потоках. При обычном подходе такой блок выполнится N раз. В данном случае N! раз.

Такое издевательство над здравым смыслом зародилось в чьей-то голове по какой вообще причине? (неужели только оттого, что питон - интерпритируемый язык, и нормальные concurency для них не годятся)

Распараллелить питонокод - полумера, если уж у вас что-то питоновое тормозит, так это оттого, что вовремя на java/C#+mono не перекинулись

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

> Такое издевательство над здравым смыслом зародилось в чьей-то голове по какой вообще причине?

Школьник, а уже такой толстый.

tailgunner ★★★★★
()
Ответ на: комментарий от RedPossum

я всё правильно понимаю

Не правильно.

Я сейчас про худший случай

В худшем случае никакие fine-grained блокировки не помогут избавиться от тормозов.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Оно касается только памяти. А вот io оно не касается.

То есть никаких фокус, контроль за побочными эффектами на совести разработчика. В принципе, нормально.

dizza ★★★★★
()
Ответ на: комментарий от RedPossum

>я всё правильно понимаю

очевидно, что нет.

нет чтобы сделать классические механизмы(семафоры те же) и отправлять потоки в дешёвое ожидание,

Здравствуй, лок.

семафоры уже давно есть. Тут речь про транзакции, котрые выполняются параллельно и если их результаты не испорчены другими транзакциями, то все хорошо, а если испорчены (чего заранее предсказать невозможно), то их по любому придется переиграть.

Я сейчас про худший случай,

который может случится в 0.000001%, а в 99.9999% этот код будет свободно параллелиться. Вопрос, что тогда будет лучше?

AVL2 ★★★★★
()
Ответ на: комментарий от AVL2

> а в 99.9999% этот код будет свободно параллелиться. Вопрос, что тогда будет лучше?

Они хотят пойти по пути lazy STM - т.е. каждая транзакция (опкод виртуальной машины, насколько я понимаю) ведет свой лог, и при успешном коммите записывает его в общую память. Это выглядит как дофига оверхеда, и может оказаться, что на 4-ядерном проце лучше GIL.

tailgunner ★★★★★
()
Ответ на: комментарий от RedPossum

всё правильно понимаю: нет чтобы сделать классические механизмы(семафоры те же) и отправлять потоки в дешёвое ожидание, разработчики PyPy решили что лучше уж заставлять поток молотить цифры несколько раз.

Нет, не правильно. Конкуренция за ресурс бывает двух типов. Названий не помню, но первый тип, это когда несколько потоков постоянно борются за ресурс, а второй, когда синхронизация нужна только для обеспечения корректности, но конкуренция происходит редко. Очевидно, транзакции идеальный вариант для второго случая. Глупо, например, делать на транзакциях blocking queue.

dizza ★★★★★
()

все не могу понять - а почему такой проблемы нет и никогда не было в других виртуальных машинах (jvm, .net)?

note173 ★★★★★
()
Ответ на: комментарий от tailgunner

>опкод виртуальной машины, насколько я понимаю

это грубо и ставит крест на всей идее.

Это выглядит как дофига оверхеда

Транзакция должна быть маленькой, просто кусочек кода.

например простейший счетчик:

counter (): start_transaction() global count+=1 end_transaction() return count

Который вызывается из жирных параллельных функций.

В данном случае мы имеем риск бОльшего количества выполнения кода count+=1 из за конкурентного доступа к текущему значению счетчика.

Но это значительно эффективнее, чем расставлять локи, запрещая выполнение жирных функций и ловя локапы на ровном месте.

AVL2 ★★★★★
()
Ответ на: комментарий от AVL2

>>опкод виртуальной машины, насколько я понимаю

это грубо и ставит крест на всей идее.

Скажи это Армину.

Транзакция должна быть маленькой, просто кусочек кода.

Меньше опкода просто ничего нет.

tailgunner ★★★★★
()
Ответ на: комментарий от dizza

контроль за побочными эффектами на совести разработчика

Вот этого бы не хотелось. Лучше пусть обучат PyPy применять STM только там где оно безопасно. А руками это плохой вариант.

Вообще тред меня заставил задуматься. Вряд ли я буду умнее разрабов pypy(скорее наоборот). Но моё видение такое: корень проблемы это общие данные. Значит GIL должен включаться только в момент доступа к общим данным.

На практике тут тыща и один нюанс. Например, может у питона байткод это тоже глобальные данные которые могут на лету изменяться(и, по-моему, так оно и есть, я видел статьи как его изменять). Но тут можно делать блокировки только на чтение или на запись.

Мне кажется всестороннее изучение этого вопроса может вызвать даже изменения в самом языке. Впрочем, ничего удивительного, многопоточность требует жертв.

Отдельная проблема это сделать код кросс-платформенным.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от dizza

Я подумал и пришёл к выводу что сделать универсальный, быстрый и эффективный(в плане скорости разработки) ЯП нельзя.

Но можно заточить его под наиболее эффективные паттерны программирования.

Собственно это путь фреймворков - облегчить решение типовых проблем типовыми солюшенами.

true_admin ★★★★★
() автор топика

Кстати, а это мысль. Можно же просто с каждым объектом ассоциировать мютекс. Пока объект локальный эту блокировку не использовать. А вот при передачи данных между тредами или при описании объекта как глобального задействовать мутекс(или как это называется, не силён в этом).

Конечно так просто это не сделать т.к. один тред может залезть в данные другого треда и это надо как-то отслеживать.

Напишу на след. неделе авторам pypy, пусть думают :).

true_admin ★★★★★
() автор топика

Нужен вариант питона с явным паралелизьмом :).

Впрочем, наверняка это можно организовать и на том что есть. Ведь есть же модуль multiprocessing который thread-API повторяет. Кстати, судя по описанию модуль неплохой раз про shared memory знает. Правда, на какой-то конфе говорилось что для IPC там используетсяя pickle, а это не очень хорошо в плане скорости при интенсивном взаимодействии.

И вообще, кому нужны быстрые треды пусть используют multiprocessing :).

true_admin ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

>Меньше опкода просто ничего нет.

ниче не понял. при чем тут опкод или не опкод? Речь об объеме и как следствие времени исполнения транзакции. Если в транзакции исполняется целая виртуальная машина, то непонятно, как транзакции вообще смогут завершиться без ошибок.

AVL2 ★★★★★
()
Ответ на: комментарий от hobbit

Как будто при взгляде на заголовок кто-то это по-другому читает :)

я читал и буду читать «пюпю», «пюфон» и т.п.

Vudod ★★★★★
()
Ответ на: комментарий от true_admin

в яве каждый объект можно использовать для синхронизации потоков:

while (flag) {
 Object task = null;
 synchronized (queue) {
  if (!queue.isEmpty())
   task = queue.pop ();
 }
 if (task != null)
  process (task);
}
Но взаимные блокировки все равно легко могут возникать

note173 ★★★★★
()
Ответ на: комментарий от note173

В руби поставили проблему лока, из-за того что большинство сишного кода на тот момент (глубокие девяностые) не было thread-safe. Потом стало как-то поздновато всё переделывать, да и можно жить с этим без проблем. В питоне подозреваю тоже самое.

В jRuby кстати абсолютно нормальная многопоточность.

tensai_cirno ★★★★★
()
Ответ на: комментарий от true_admin

>> По определению транзакции можно откатывать.

STM это software transactional memory. Оно касается только памяти. А вот io оно не касается. Поэтому если есть сайд-эффекты то будут проблемы при откате.


Понятно дело что STM это транзакционная память. Только у нас IO никакого отношения к памяти не имеет. Транзакции обязаны уметь либо коммитится, либо откатываться, иначе это никакая не транзакция.

tensai_cirno ★★★★★
()
Ответ на: комментарий от note173

Это то, что сделано как идея. Не для повседневной работы, а с целью реализовать и проверить на вшивость некую идею, концепт.

AVL2 ★★★★★
()
Ответ на: комментарий от tensai_cirno

В данном случае поскольку транзакции работают с отдельной копией данных, откат, это значит просто забыть про этот процесс. Так что откат есть.

AVL2 ★★★★★
()
Ответ на: комментарий от tailgunner

ну слава богу. Я уж думал, что-то пропустил.

AVL2 ★★★★★
()
Ответ на: комментарий от tensai_cirno

В принципе можно ещё использовать распределённую транзакцию, которая захватит io. С другой стороны, если РТ используется в пределах одной системы, с архитектурой приложения явно что-то не так.

anonymous
()
Ответ на: комментарий от anonymous

код пользовательский. Совсем не обязательно это именно инкремент.

AVL2 ★★★★★
()
Ответ на: комментарий от Vudod

я читал и буду читать «пюпю», «пюфон» и т.п.

правильно пай-пай и пайтон! ин инглиш :)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.