В этом посте от создателя Python, Гвидо Ван Россума, упоминается ранняя попытка удалить GIL из Python:
Это было опробовано ранее, и результаты оказались неутешительными, поэтому я не хотел бы приложить к этому много усилий сам. В 1999 году Грег Стейн (вместе с Марком Хэммондом?) Выпустил форк Python (я полагаю, 1.5), который удалил GIL, заменив его мелкозернистыми блокировками на всех изменяемых структурах данных. Он также представил патчи, которые убрали многие из опор глобальных изменяемых структур данных, которые я принял. Однако после сравнительного анализа было показано, что даже на платформе с самым быстрым блокирующим примитивом (в то время Windows) он замедлял однопоточное выполнение почти в два раза, а это означает, что на двух процессорах вы можете получить чуть больше работы сделано без GIL, чем на одном процессоре с GIL. Этого было недостаточно, и патч Грега исчез в забвении. (См. Рецензию Грега на представление.)
Я не могу спорить с реальными результатами, но мне действительно интересно, почему это произошло. Предположительно, главная причина, по которой удаление GIL из CPython является настолько сложной, заключается в системе управления памятью подсчета ссылок. Типичная программа Python будет вызывать Py_INCREF
и Py_DECREF
тысячи или миллионы раз, что делает его ключевой момент конкурирующего если мы должны были обернуть пряди вокруг него.
Но я не понимаю , почему добавление атомных примитивов бы замедлить единую резьбовую программу. Предположим, мы только что изменили CPython, чтобы переменная refcount в каждом объекте Python была атомарным примитивом. И тогда мы просто делаем атомарный инкремент (инструкция извлечения и добавления), когда нам нужно увеличить счетчик ссылок. Это сделало бы подсчет ссылок на Python безопасным для потока и не привело бы к снижению производительности однопоточного приложения, поскольку не было бы конфликта блокировок.
Но, увы, многие люди, которые умнее меня, пытались и терпели неудачу, поэтому, очевидно, я что-то здесь упускаю. Что плохого в том, как я смотрю на эту проблему?
Ответы:
Я незнаком с Greg Stein Python fork, поэтому, если хотите, обесцените это сравнение как умозрительную историческую аналогию. Но это был именно исторический опыт многих инфраструктурных кодовых баз, переходящих от однопоточных к многопоточным реализациям.
Практически каждая реализация Unix, которую я изучал в 1990-х годах - AIX, DEC OSF / 1, DG / UX, DYNIX, HP-UX, IRIX, Solaris, SVR4 и SVR4 MP, - все проходила именно через этот тип, который мы ввели более тонкая блокировка - теперь она медленнее !! проблема. СУБД, за которыми я следовал - DB2, Ingres, Informix, Oracle и Sybase - тоже прошли через это.
Я слышал, что «эти изменения не замедлят нас, когда мы запускаем однопоточную» миллион раз. Это никогда не сработает. Простой акт условной проверки "многопоточный ли у нас или нет?" добавляет реальные накладные расходы, особенно на высокотрубных процессорах. Атомарные операции и случайные спин-блокировки добавляются для обеспечения целостности общих структур данных, которые нужно вызывать довольно часто, и они очень медленные. Примитивы блокировки / синхронизации первого поколения также были медленными. Большинство команд по внедрению в конечном итоге добавляют несколько классов примитивов с различными «сильными сторонами», в зависимости от того, сколько защиты от блокировки требуется в разных местах. Затем они понимают, что изначально они закрывали блокирующие примитивы на самом деле не в том месте, поэтому им пришлось профилировать дизайн вокруг найденных узких мест, и систематически рото-до. Некоторые из этих препятствий в конечном итоге получили ускорение ОС или оборудования, но вся эта эволюция заняла 3-5 лет, минимум. Между тем, версии MP или MT хромали в плане производительности.
В противном случае сложные команды разработчиков утверждают, что такие замедления в основном являются постоянным, неразрешимым фактом жизни. Например, IBM отказалась от AIX с поддержкой SMP, по крайней мере, в течение 5 лет после соревнования, утверждая, что однопоточность была просто лучше. Sybase использовал одни и те же аргументы. Единственная причина, по которой некоторые команды в конечном итоге пришли в себя, заключалась в том, что однопотоковая производительность больше не могла быть разумно улучшена на уровне процессора. Они были вынуждены либо пойти MP / MT или принять все более неконкурентоспособный продукт.
Активный параллелизм труден. И это обманчиво. Каждый врывается в это, думая, что «это не будет так плохо». Затем они попали в зыбучие пески, и им пришлось пробиться сквозь них. Я видел это по крайней мере с дюжиной известных брендов, хорошо финансируемых, умных команд. Как правило, после выбора многопоточности потребовалось не менее пяти лет, чтобы «вернуться туда, где они должны быть, с точки зрения производительности» с продуктами MP / MT; большинство из них все еще значительно улучшали эффективность / масштабируемость MP / MT даже через десять лет после перехода.
Таким образом, мое предположение состоит в том, что, без одобрения и поддержки GvR, никто не взял на себя долгий шаг за Python и его GIL. Даже если бы они сделали это сегодня, это был бы период Python 4.x, прежде чем вы сказали бы: «Ух ты! Мы действительно преодолели горб MT!»
Возможно, есть какая-то магия, которая отделяет Python и его среду выполнения от всего остального программного обеспечения для инфраструктуры - все языковые среды выполнения, операционные системы, мониторы транзакций и менеджеры баз данных, которые были раньше. Но если так, это уникально или почти так. Всем остальным, кто удаляет GIL-эквивалент, потребовалось пять с лишним лет упорных, самоотверженных усилий и инвестиций, чтобы перейти от MT-not к MT-hot.
источник
Еще одна дикая гипотеза: в 1999 году Linux и другие Unices не имели производительной синхронизации, как сейчас
futex(2)
( http://en.wikipedia.org/wiki/Futex ). Те пришли примерно в 2002 году (и были объединены в 2.6 около 2004 года).Поскольку все встроенные структуры данных должны быть синхронизированы, блокировка стоит дорого. Ᶎσᶎ уже указывал, что атомные операции не нужны дешево.
источник