LINUX.ORG.RU

многопоточный питон

 ,


0

1

Господа, в этот праздничный день, помогите, пожалуйста, создать змея с многими головами! :)

file является гиперспектральным изображением (т.е. избражение, где каждый пиксель представляет собой спектр). hs - библиотека hyperspy для работы с этими изображениями. Функция multifit приближает каждый из спектров в пикселе при помощи функции войта. Так как файлов больше одного, а процессинг каждого занимает у моего компьютера примерно 2 минуты, я бы хотел это сделать сразу в несколько потоков. Есть вот такой код:

from multiprocessing import Pool

def fit_file(file):
    image = hs.load(file).T
    m = image.create_model()
    v1 = hs.model.components1D.Voigt()
    m.append(v1)
    m.multifit()
    return file, v1

with Pool(processes=4) as pool:
    v = list(pool.map(fit_file, files[:2]))

В результате получаю:

---------------------------------------------------------------------------
MaybeEncodingError                        Traceback (most recent call last)
<timed exec> in <module>

/usr/lib/python3.7/multiprocessing/pool.py in map(self, func, iterable, chunksize)
    266         in a list that is returned.
    267         '''
--> 268         return self._map_async(func, iterable, mapstar, chunksize).get()
    269 
    270     def starmap(self, func, iterable, chunksize=None):

/usr/lib/python3.7/multiprocessing/pool.py in get(self, timeout)
    655             return self._value
    656         else:
--> 657             raise self._value
    658 
    659     def _set(self, i, obj):

MaybeEncodingError: Error sending result: '[('filename', <ZLP (Voigt component)>)]'. Reason: 'KeyError('__getstate__')'

Как я понимаю это связано сериализацией, но я не так хорошо разбираюсь в питоне чтобы решить эту проблему. Пробовал ещё ipython.parallel и Ray, но обычно сталкивался с такой же (ну или внешне похожей) проблемой. Всё это выполняется Jupyterlab.

Может кто-то подсказать что делать?

★★★★★

Последнее исправление: vitruss (всего исправлений: 1)
Ответ на: комментарий от vitruss

Не сработает, нет её.

Ты не можешь передавать сложные объекты между процессами. Обрабатывай каждый файл в отдельном процессе целиком.

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

если ты вызываешь внешние сишные библиотеки, они прекрасно многопоточно работают через питоновские потоки

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

Нет. Что за околесица? Самостоятельно запущенные потоки, конечно, работать будут, а питонические работают по очереди.

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

запусти вот это и охреней:

import concurrent.futures,ctypes
lc=ctypes.CDLL('libc.so.6')
ex=concurrent.futures.ThreadPoolExecutor(max_workers=4)
ex.map(lc.sleep,(10,10,10,10))
anonymous
()
Ответ на: комментарий от anonymous

Ты можешь и просто time.sleep вызвать, оно тоже будет спать одновременно, лол. Это не CPU-boud задача. Сисколы, io (сисколв же), естественно, спокойно работают в отдельном потоке, не блокируя исполнение остальных.

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

А как потом слить результат в один процесс? Чтобы обрабатывать всю совокупность данных.

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

Я намеренно понимаю, что если вместо сишной функции тут будет арифметика от numpy оно тоже будет работать практически без оверхеда и ещё и иметь доступ к переменным родителя?

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

питоновский код НЕ будет выполняться параллельно, разумеется

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

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

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

Так, как только вернёшься в питон, сразу вся многопоточность превратится в тыкву. То есть ТСу нужен на сях тот код который он параллелит, иначе смысла нет.

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

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

не зная ничего про hyperspy, я бы попробовал потоки и посмотрел станет ли работать так быстрее чем последовательно, там и так половина не на питоне

Ты не можешь передавать сложные объекты между процессами

Интересно, что это за «сложные объекты» такие, которые между процессами нельзя передавать? Вопрос риторический, просто намек.

ei-grad ★★★★★
()
Ответ на: комментарий от vitruss

Попробуй. Просто from multiprocessing.pool import ThreadPool as Pool. Главный вопрос отпускает ли оно там GIL внутри на CPU-bound участках. Можно посмотреть в исходниках, но проще просто проверить.

ei-grad ★★★★★
()
Последнее исправление: ei-grad (всего исправлений: 1)

Ещё из «подсказать» - что тебе потом с этими объектами дальше делать надо? Если просто вытащить какие-то данные, то можно попробовать эту часть тоже утащить в тот процесс где multifit вызывается.

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

Не работает ThreadedPool, причём ошибка какая-то странная, которая не проявляется в последовательных вычислениях и других параллельных вариантах.

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

vitruss ★★★★★
() автор топика
Последнее исправление: vitruss (всего исправлений: 1)

Возьми например Java. Там, во-первых, есть многопоточность нормальная. Во-вторых, возможно, что оно твои пиксели будет считать раз в 500 быстрее даже на одном процессоре.

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

Угу, вероятно либа не thread-safe, грусть-пичаль :-/.

ei-grad ★★★★★
()
Ответ на: комментарий от WitcherGeralt
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<timed exec> in <module>

/usr/lib/python3.7/multiprocessing/pool.py in map(self, func, iterable, chunksize)
    266         in a list that is returned.
    267         '''
--> 268         return self._map_async(func, iterable, mapstar, chunksize).get()
    269 
    270     def starmap(self, func, iterable, chunksize=None):

/usr/lib/python3.7/multiprocessing/pool.py in get(self, timeout)
    655             return self._value
    656         else:
--> 657             raise self._value
    658 
    659     def _set(self, i, obj):

/usr/lib/python3.7/multiprocessing/pool.py in worker(inqueue, outqueue, initializer, initargs, maxtasks, wrap_exception)
    119         job, i, func, args, kwds = task
    120         try:
--> 121             result = (True, func(*args, **kwds))
    122         except Exception as e:
    123             if wrap_exception and func is not _helper_reraises_exception:

/usr/lib/python3.7/multiprocessing/pool.py in mapstar(args)
     42 
     43 def mapstar(args):
---> 44     return list(map(*args))
     45 
     46 def starmapstar(args):

<timed exec> in fit_file(file)

AttributeError: 'BaseSignal' object has no attribute 'create_model'

Хотя для однопотока такой проблемы нет совсем. Попробовал так же concurrent.futures с ними он обрабатывает один из 2-х файлов (сейчас пробую на 2-х паралелльно), а на второй валится с той же ошибкой.

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

Смотрел, но не знаю с какой стороны подойти. Можно, в принципе, данные преобразовать в массив dask, обработать и потом обратно, но это как-то странно выглядит. Хотя сейчас начал читать подробнее, может и получится.

vitruss ★★★★★
() автор топика
Последнее исправление: vitruss (всего исправлений: 1)

с потоками точно не станет быстрее из-за Gil: у тебя cpu-bound операции.

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

Я попробую ещё сегодня вечером, может и получится…

vitruss ★★★★★
() автор топика
Ответ на: комментарий от ei-grad

Такие, затраты на передачу которых перекрывают выгоду от передачи.

Siborgium ★★★★★
()

Я дико извиняюсь, ибо...

тоже как бы спектроскопией когда-то то-сё, но что это, простите, за такой набор данных, что уходит 2 минуты на файл?

Если в профиле одна линия, то хоть сколько там точек по спектру, задай подходящее начальное приближение, и оно быстро зафитит. Или оно перебирает всевозможные Лоренцевы и Гауссовы распределения, делая свертку и подбирая профиль? Тогда может его в два прохода - грубое приближение (которое, поди, хватит для бОльшей части последующего анализа) и определение механизма уширения?

У вас спектральная линейка, или матрица, какие файлы то?

sshestov ★★
()
Ответ на: Я дико извиняюсь, ибо... от sshestov

Там изображение, примерно 2048х2048 пикселей, в каждом пикселе спектр, 2048 точек, в данный момент на каждом спектре один пик, примерно в одном месте и примерно равной интенсивности, но не точно и этот пик приближается функцией Войта (которая имеет в свою очередь имеет 4 параметра и вычисляется не очень просто). Операция, включая чтение с диска (HDD) и вычисление параметров для всех пикселей, занимает примерно 1.5 минуты на одном ядре Core i5-6500, причем это делается сначала для одного пикселя, а потом параметры этого пикселя используются как начальные значения для всех остальных, что конечно ускоряет операцию. Мне не сложно запустить всё это считаться за ночь и просто сохранить результат, но очень интересно понять можно ли это сделать параллельно на питоне.

vitruss ★★★★★
() автор топика
Последнее исправление: vitruss (всего исправлений: 2)
Ответ на: комментарий от vitruss

Интересная у вас ситуация...

Давайте отделим мух от котлет.

  1. Если у вас действительно файл размером 2048х2048х2048 и в каждой точке float (что типа 4 байта), то сам файл занимает как бы 34 Гигабайта. Это, скажем прямо, не мало и только его чтение с диска занимает порядочно времени. А также начинаются всякие ограничения с памятью, когда копию его не сделаешь, в MPI-режиме не запустишь, два параллельных независимых процесса не сделаешь.

  2. Это что у вас за спектрограф такой, который выдает 2Д изображение со спектрами, да еще в таком разрешении, по каждому направлению. Как они регистрируются то? Сканируется по спектру, или по пространству (вроде как детекторы у нас - ПЗС-ки и CMOS-ы до сих пор двумерные)? Если время выполнения важно и хочется стандартных библиотек, то как вариант - в начале «обработки» ужать файл до размеров, которые соответствуют физическому (оптическому или спектральному, что суть одно и то же) разрешению спектрографа.

  3. Можно попробовать выяснить что будет, если перейти от Войта к более простой функции (если честно, я забыл как правильно получать Войта и нужно ли каждый раз делать свертку. Если с ваших слов «4 параметра» - то не нужно). Замена Войта на Гаусса или Лоренца дает принципиальное ускорение?

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

[code=python]

def fit_file(file): image = hs.load(file).T m = image.create_model() v1 = hs.model.components1D.Voigt() m.append(v1) m.multifit() return file, v1

[/code]

Там где цикл по пикселям и подстановка первого пиксела как начальные приближение в остальные? Видимо «под капотом»? У вас по много раз передача массивов туда-сюда не происходит? Image - это по-видимому исходная спектрограмма (или как она правильно будет?), a m - спектральный фит. На какой функции тормозит?

sshestov ★★
()
Последнее исправление: sshestov (всего исправлений: 1)
Ответ на: комментарий от Shadow

Оно под капотом и так в numpy array с 3мя осями и использует (в конечном итоге) scipy.optimize. Вроде, scipy умеет в несколько потоков приближение методом наименьших квадратов, но у меня обычно только в один поток выходило, только в редких случаях видел больше одного ядра нагружено.

vitruss ★★★★★
() автор топика
Ответ на: Интересная у вас ситуация... от sshestov

Давайте тогда по порядку, раз интересно. Эти изображения получены на сканирующем просвечивающем электронном микроскопе, т.е. берётся пучёк электронов «размером» порядка 0.5 Å и с разбросом энергии порядка 40 мэВ (при общей энергии 80 кэВ) и сканирует образец. На вылете из образца электроны теряют часть энергии и, попадая в детектор, регистрируется спектр их энергий там (там стоит магнитная призма, 2048 каналов, в моём разрешение 7.7 мэВ), которая, для большинства электронов и не меняется вовсе (в моём случае образец толщиной ~2 атома, хотя это делают и для гораздо более толстых образцов). Таким образом спектр можно снять с пространственным разрешением меньше одного атома, и с достаточно высокой скоростью, кажется тут это примерно 100 мс на пиксель.

Действительно, 2048х2048 это разрешение обычного изображения, я написал размеры по памяти и не учёл, что для спектров была выбрана область примерно 100х100 пикселей. Размер файла при этом порядка 80 МБ, что соответствует размеру в те самые 34 ГБ для изображения 2048х2048.

Замена Войта принципиальное ускорение даст, причём существенное, только мне этого не надо. Мне нужен именно Войт для описания пика, потому что этот пик даёт вклад гораздо дальше своей полуширины, там где лежат гораздо менее интенсивные, но интересные для науки линии спектра.

Теперь про код. Действительно, в том фрагменте что приведён, нет «приближаем один спектр и используем его параметры для остальных». Я этот участок вырезал для того чтобы не нагружать первый пост, поскольку это не имеет принципиального значения для того чтобы сделать этот код параллельным. Больше всего времени занимает этот самый multifit, я тут доверяю библиотеке, но думаю что оно приближает спектры по очереди, потому что основано на scipy.optimize и не нагружает процессор больше чем на 100% одного ядра.

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

vitruss ★★★★★
() автор топика
Последнее исправление: vitruss (всего исправлений: 1)
Ответ на: комментарий от vitruss

В терминах numpy - когда ты функцию не последовательно к каждой строке таблицы применяешь, а переделываешь её на «векторную» и применяешь ко всей матрице разом. В доках numpy написано.

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

Ок, я даже как-то не подумал, что спектры бывают не оптические (или рентгеновские или EUV, что в общем одно и то же). Тогда замечание про Войта и потенциальный размер файлов снимаю.

Но если картинка 100х100х2048 и 80 МБ, тогда это не много, и странно, что уходит 2 мин на файл.

Если мы считаем, что библиотека идеальна, да и еще принимаем комментарии коллег про «python не умеет в многопоточность» - то выхода как бы и нет.

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

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

sshestov ★★
()

Тред не читал. Используй новые сабинтерпретаторы, которые похожи на Коурутины в Го. Будет много маленьких отдельных ГИЛов без оверхеда и геморроя мультипроцессинга.

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

новые сабинтерпретаторы, которые похожи на Коурутины в Го

Анонимусам запретили входить на лор трезвыми?

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

Собственно, даже можно попробовать развернуть массив 100x100x2048 в двумерный 10000x2048 или транспонированный. Может получиться (а может и не получиться; но попробовать стоит, ставлю шоколодку) порядочное ускорение.

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

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

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