python. отладка тредов.

Phoenix

Как можно посмотреть, к какому объекту обращаются сразу 2 треда?
Прога валится с сообщением "программа выполнила недопустимую операцию и будет закрыта"
Нашёл всякие info thread, frame, но как их вызвать-то, если прога уже сломалась.
Если запускать "python -m pdb myprog.py " - то тоже самое, что и без отладчика.

spensnp

к какому объекту обращаются сразу 2 треда
нечего на них смотреть. mutex-ами закрывай. все.

Phoenix

ну вот, что именно закрывать-то я и хочу узнать?
всё закрыто вроде. Кода много. По каждой строчке гулять - нереально.

kokoc88

Прога валится с сообщением "программа выполнила недопустимую операцию и будет закрыта"
Странно, это обычное WIN32 падение, но разве питон может так падать при каких-то обстоятельствах?

yroslavasako

отладочный print - не вариант? Его можно через декоратор включать в классы и функции

Dasar

Странно, это обычное WIN32 падение, но разве питон может так падать при каких-то обстоятельствах?
треды без синхронизации приводят к затертой памяти (к обращению по невалидному адресу и от платформы, языка и т.д. - это не зависит.

kokoc88

треды без синхронизации приводят к затертой памяти (к обращению по невалидному адресу и от платформы, языка и т.д. - это не зависит.
Интересно, и как, например, уронить программу на C#? Кажется, я не встречался с такими ошибками...

Maurog

треды без синхронизации приводят к затертой памяти (к обращению по невалидному адресу и от платформы, языка и т.д. - это не зависит
вообще-то, языки высокого уровня (скриптовые в частности) не должны допускать подобных падений. потоки в таких языках тоже высокоуровневые. падения возможны при написании расширений (.dll потому что они написаны на C\C++ (на низкоуровневом языке)
почему в питоне это не так?

yroslavasako

почему в питоне это не так?
проблемы cpython в работе с многопоточными приложениями общеизвестны. Можно попробовать тот же проект запустить под jython или под pypy

Dasar

Интересно, и как, например, уронить программу на C#? Кажется, я не встречался с такими ошибками...
поднять два (а лучше 20) треда и начать активно работать с одними и теми же данными.
через некоторое время программа падает по exception-у ExecutionEngineException.
на втором .net-е грохалось стабильно, особенно при наличие двух реальных процессорных ядер, на более поздних версиях не проверял.

Dasar

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

Dasar

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

yroslavasako

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

kokoc88

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

Dasar

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

kokoc88

afaik, VM ничего не проверяют(кроме null) - они опираются на гарантию что невалидных адресов не может быть в принципе.
Если есть такая гарантия, то тогда как твой пример может упасть?

Dasar

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

Dasar

Если есть такая гарантия, то тогда как твой пример может упасть?
то, что несколько одновременно работающих тредов могут эту гарантию нарушить, например: Array.Copy с параллельным чтением адресов из другого потока

bleyman

Wait-wait-wait, то есть ты утверждаешь, что код, прошедший через верификатор, удовлетворяющий всем security policy etc, может посредством подобного нехитрого трюка получить указатель в произвольное место памяти?
Ну-ну.
То есть я, конечно, не исключаю возможности того, что тебе случайно удалось наткнуться на баг (который именно баг, а не by design). Причём он вовсе не был обязан иметь именно такую природу. Или может быть ты вообще случайно какой-нибудь unsafe код юзал, а то и вовсе п/инвоук!
Информация к размышлению, кстати: копирование указателя всегда атомарная операция. Потому что размер указателя есть размер машинного слова.
Back to topic: вообще говоря в чистом питонокоде, исполняющемся на CPython ничего подобного быть не может в принципе, так как в нём есть GIL (Global Interpreter Lock который превращает его в sequentially executed. То есть: раз в N (==200 по дефолту) байткодных инструкций интерпретатор отпускает и тут же пытается забрать обратно особый глобальный лок. При этом например доступ к элементу хэштейбла является одной инструкцией.
Но есть нюансы: extension classes пишутся на С, имеют прямой доступ ко всей памяти интерпретатора (то есть например ctypes позволяет завести массив на надцать интов и передать поинтер на него в дллку а так же любят порой отпустить GIL перед выполнением длительной операции (при работе с блокирующимся IO это делается всегда, например). Соответственно если ОП что-то такое использует, то там могут быть разные баги!

Vlad77

Информация к размышлению, кстати: копирование указателя всегда атомарная операция. Потому что размер указателя есть размер машинного слова.
Допустим не выровнил ты свой массив, а копировать для скорости собрался выровненными машинными словами + байтики на границах.

Dasar

Wait-wait-wait, то есть ты утверждаешь, что код, прошедший через верификатор, удовлетворяющий всем security policy etc, может посредством подобного нехитрого трюка получить указатель в произвольное место памяти?
вообще-то в security policy запрещено создание тредов

Dasar

Информация к размышлению, кстати: копирование указателя всегда атомарная операция. Потому что размер указателя есть размер машинного слова.
это именно гарантируется? с учетом всех кэшей, в том числе и кэшей между разными процессорами и т.д.?

Andbar

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

psihodog

Информация к размышлению, кстати: копирование указателя всегда атомарная операция. Потому что размер указателя есть размер машинного слова.
и на amd64?

bleyman

Чуваки, вы что?
Да, и на amd64 чтение из памяти в регистр и наоборот — атомарная операция. То есть получить смесь двух пойнтеров физически невозможно. Да, между чтением и записью может пройти неопределённое время, да, чтение может забрать данные не из памяти, а из одного из кэшей, и запись — записать, но есть необоримый закон природы: кусок данных размером с машинный регистр читается или пишется атомарно.
Процессорам типа х86-64 наверное приходится специально предпринимать ухищрения, чтобы гарантировать это для nonaligned accesses (другие просто хардверный эксепшен кидают) хотя хм, я не могу подтвердить эти свои слова. Со своей стороны я пожалуй могу гарантировать, что массив пойнтеров в дотнетах is correctly aligned. Любая попытка обойти это через "массив байтиков", требует unsafe code со всеми вытекающими.
: ну, а если через предоставленные осью? То есть например Ctrl-C приходит в отдельный тред, файналайзеры вызываются из отдельного треда, это всё тоже всегда запрещено для verifiable code?

Dasar

То есть например Ctrl-C приходит в отдельный тред,
это что такое?

Maurog

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

yroslavasako

это что такое?
SIGINT

Dasar

SIGINT
и как это сочетается с тем же .net-ом, про который, например, шла речь?

vall

не думаю что они гарантируют атомарность если слово легло поперёк кэшлайнов. шерстить спеки лень.

Vlad77

шерстить спеки
8.1.1 Guaranteed Atomic Operations

Dasar

net 3.5 сходу уронить не получилось, но надо еще проверить на многопроцессорном 64-битном

Anturag

Да, и на amd64 чтение из памяти в регистр и наоборот — атомарная операция.

Это неправда для x86 архитектур. Правда в том, что атомарными операциями будут чтение из aligned памяти в регистр общего назначения и наоборот.

Dasar

Правда в том, что атомарными операциями будут чтение из aligned памяти в регистр общего назначения и наоборот.
а movsd какой-нибудь?

Anturag

а movsd какой-нибудь?

Не является атомарной.

psihodog

Ну, слава богу. А у каких операций были проблемы у амд64 с атомарностью? Я что-то слышал краем уха, но не запомнил.

vall

amd64 это название системы команд — синоним для x86-64

Phoenix

вообще-то в security policy запрещено создание тредов

а можно ссылку?
т.е. threading для прикола?
или это какая-то общая политика, а не питоновская?

Dasar

или это какая-то общая политика, а не питоновская?
это про .net речь шла

Phoenix

Back to topic: вообще говоря в чистом питонокоде, исполняющемся на CPython ничего подобного быть не может в принципе, так как в нём есть GIL (Global Interpreter Lock который превращает его в sequentially executed. То есть: раз в N (==200 по дефолту) байткодных инструкций интерпретатор отпускает и тут же пытается забрать обратно особый глобальный лок. При этом например доступ к элементу хэштейбла является одной инструкцией.

Почитал про это тут http://docs.python.org/c-api/init.html
(кстати, там пишут, что 100. )
Отпало много вопросов по поводу странных локов до этого.
В питон-тредах я окончательно разочаровался. Придётся на разные процессы разделять.

bleyman

Ну, питонотреды надобно исключительно для concurrency использовать, а не для parallelism.
И для concurrency они тоже ужасно сосут на мультикорах, потому что наличие единственного CPU-bound thread означает, что все другие треды на соседних корах задолбаются у него отбирать GIL, потому что он его как правило забирает обратно раньше, чем другое ядро успевает проснуться. В 3.2 какой-то чувак сделал новый, более вменяемый механизм (но суть осталась та же но потом обнаружил, что всё равно хуита: теперь ИО треды нормально забирают ГИЛ обратно, но поскольку вообще каждая ИО операция, в том числе и те, которые в подавляющем большинстве случаев не блокируются — send, write, — его отпускают, получается, что они используют только небольшой кусочек своего таймслайса. После этого чувак прихачил поверх priority boost, вроде теперь работает нормально, но и когда оно в стейбл третьего питона попадёт, неизвестно, а уж когда его портируют обратно во второй, и портируют ли вообще (потому что если честно третий никому нафиг не сдался неизвестно.
Так что используй процессы, чё. Это в общем-то и правильней, глюков меньше и всё такое. Под виндой можно использовать Pool, чтобы не беспокоиться насчёт тормозов при создании процессов. Кстати, в subprocessing'e на самом деле огромнейшее количество полезных штук, прочитай мануал про него сначала, чтобы не изобретать велосипеды!
А ещё кстати: я недавно узнал, что я наврал про атомарность например доступа к элементу хэштейбла, нифига оно не атомарно для типов с переопределённым __eq__ и __hash__. То есть падать ничего не должно всё равно, но интересные баги теоретически могут появиться.

ava3443

почитал ссылку выше, позор питону (CPython) конечно! не ожидал такого...
даже JavaScript-интерпретатор в мозилле и то по-нормальному thread-safe (используем на многоядерных машинах, как раз в том числе и для parallelism хотя сама мозилла (браузер) его в 99% случаев из одного потока использует...

bleyman

Ну, на это есть причины.
Питон дико динамический, там практически всё можно подпатчить в рантайме. Вплоть до залезания немытыми руками в глобальный список импортированных модулей. Благодаря этому существует Psyco (JIT compiler Cython и забавные штуки вроде модуля, добавляющего goto и comefrom — всё на уровне приложения, без необходимости перекомпилировать интерпретатор.
У этого есть неприятное следствие: скорость доступа к внутренним структурам интерпретатора и вообще ко всему является основным тормозящим фактором. Потому что он к ним доступается постоянно. Ну то есть когда ты засовываешь объект в хэштейбл, оно доступается к методам __eq__ или __hash__ его класса и всех остальных родительских классов. Поэтому если синхронизировать каждый такой доступ, интерпретатор мгновенно затормаживается в разы.
Когда-то лет пять-восемь назад какой-то чувак подпатчил интерпретатор, чтобы он стал thread safe — получил двукратные тормоза на single core и более или менее такую же производительность, как у обычного CPython, в хорошо паралеллящихся задачах на квадкоре. Разумеется, смысла такое делать нет.
Более того, где-то год назад чуваки в гугле начали делать свою реимплементацию интерпретатора, Unladen Swallow, поверх LLVM. Одной из целей было избавиться от GIL. Ну и опять, несколько месяцев назад они заявили, что вообще оно у них работает теперь довольно быстро, но попытка избавиться от GIL привела к приблизительно тому же результату, так что забейте, чуваки, лучше использовать быстрый лок-фри интерпретатор в отдельных процессах.
И это правильно, по-моему. Дело даже не в том, что синхронизация тредов порождает столько трудноотлавливаемых ошибок, что уже поэтому её лучше избегать, а в том, что мы сейчас в довольно нетипичном времени с точки зрения архитектуры процессоров находимся. Типа, что есть возможность более или менее эффективно доступаться к shared memory с четырёх ядер, например. Я уверен, что для 32 ядер, а то и 16, такой возможности уже не будет, вообще. Программы для 32-ядерных процессоров будут программироваться исключительно через а) passing immutable messages, б) publishing immutable shared memory. Соответственно текущая питоновская модель отлично ляжет на это будущее железо, а все попытки её "исправить" окажутся пустой тратой времени, потому что не лягут.

pilot

Более того, где-то год назад чуваки в гугле начали делать свою реимплементацию интерпретатора, Unladen Swallow, поверх LLVM. Одной из целей было избавиться от GIL. Ну и опять, несколько месяцев назад они заявили, что вообще оно у них работает теперь довольно быстро, но попытка избавиться от GIL привела к приблизительно тому же результату, так что забейте, чуваки, лучше использовать быстрый лок-фри интерпретатор в отдельных процессах.
Отсюда
Global Interpreter Lock
From an earlier draft of this document:
In addition, we intend to remove the GIL and fix the state of multithreading in Python. We believe this is possible through the implementation of a more sophisticated GC system, something like IBM's Recycler (Bacon et al, 2001).
Our original plans for dealing with the GIL centered around Recycler, a garbage collection scheme proposed by IBM researchers. This appeared at first blush to be an excellent match for Python, and we were excited about prospects for success. Further investigation and private communications revealed that safethread, an experimental branch of CPython, had also implemented Recycler, but with minimal success. The author relayed that he had demonstrated a speed-up on a dual-core system, but performance degraded sharply at four cores.
Accordingly, we are no longer as optimistic about our chances of removing the GIL completely. We now favor a more incremental approach improving the shortcomings pointed out by Dave Beazley, among others. In any case, work on the GIL should be done directly in mainline CPython, or on a very close branch of Python 3.x: the sensitive nature of the work recommends a minimal delta, and doing the work and then porting it from 2.x to 3.y (as would be the case for Unladen Swallow) is a sure-fire way of introducing exceedingly-subtle bugs.
Longer-term, we believe that CPython should drop reference counting and move to a pure garbage collection system. There is a large volume of classic literature and on-going research into garbage collection that could be more effectively exploited in a pure-GC system. Even without the GIL, the current volume of refcount update operations will make scaling effectively to many-core systems a challenge; we believe a pure garbage collection scheme would alleviate these pressures somewhat.

yroslavasako

В питон-тредах я окончательно разочаровался. Придётся на разные процессы разделять.
а как соотносятся питон-треды и PyQt треды? Одно реализовано через другое?

Phoenix

да какая разница.
если передать в pyqt тред байт код, то тамбудет что-то типа
qt-тред
{
py_allow_threads
какая-то qt-хрень
py_disallow_threads
user-функция (которая в байт коде и юзает GIL)
py_allow_threads
другая qt-хрень
py_disallow_threads
}
Короче, gil по умолчанию залочен и в этом идея. И только иногда его можно разлочить на уровне с-вызовов (не знаю как это правильно назвать.)
т.е. любая фукнция на нижнем уровне написана так
function
{
     py_allow_threads
     делаем что-то, что точно уже не будет ни с чем конфликтовать (например, запись в файл)
     py_disallow_threads
}

Phoenix

И это правильно, по-моему. Дело даже не в том, что синхронизация тредов порождает столько трудноотлавливаемых ошибок, что уже поэтому её лучше избегать
согласен. С самого начала мучительно я думал, что от тредов надо уходить, но как-то не хватало веской причины. Тут даже несколько топиков было про это.
Хорошо, что оно сейчас упало, а не через пол-года.
всем ещё раз спасибо!

yroslavasako

Новость вышла про PyPy 1.2, говорят даже быстро работает (перестал отставать от CPython)
Оставить комментарий
Имя или ник:
Комментарий: