Pull to refresh

Comments 35

К сожалению, мне не удалось создать работающие ссылки внутри статьи. Хабр удаляет теги \<\a id="..."> в markdown (и здесь мне не очень удалось написать это текстом), а когда я пытался вставить html, возникали проблемы с форматированием кода. Если кто-то умеет делать нормальные перекрёстные ссылки здесь, то поделитесь, пожалуйста!
It is probably impossible in Python to stop a function and resume it at the given point. Inform the author if you know how to do that.


Я возможно не понял постановку вопроса, но в питоне есть асинхронное выполнение: docs.python.org/3/library/asyncio.html
Или можно использовать параллелизм с multiprocessing

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

1) Делаем у последовательности метод fork
2) Значения передаем только когда все форки запросили его

Весь параллелизм прячем внутри.
Спасибо, я буду думать об этом. Я не понимаю в деталях, как это работает. У нас есть поток, генератор. Что значит передаём значение, когда его все запросят? Это всё равно управление не из пользовательской функции. Можно обсудить после того как я 2ю часть напишу, там тоже подобный подход. Управление в последовательности находится. В itertools есть подобный метод, как размножить генератор, но он его весь загружает в память.

Я вспомнил, что в питоне функции не являются reentrant, я прежде всего в этом смысле писал. Функция не closure.
1) Начну с конца, что вы помимаете под closure? Потому что по стандартному определению они вполне closure:
def outerFunction(text): 
    text = text 
  
    def innerFunction(): 
        print(text) 
  
    innerFunction() 
  
if __name__ == '__main__': 
    outerFunction('Hey!') 

2) reentrant — GIL в питоне обходится через новые интсансы интерпретаторов, так работает multiprocessing, в будущем обещают более качественную поддержку на уровне ядра, кроме того есть async.
3) Смотрите есть два типа моделей push и pull.
Если у вас есть цепочка, то в случае push модели вычисления инициирует первый член цепочки и он пихает данные дальше, в данном случае форк делается легко, он просто в определенный момент пихает данные не в одну цепочку, а в две.
В случае pull модели вычисления инициирует последний член цепочки, соответственно если мы хотим разветвтить цепочку, тот уже надо думать, что делать: или запускать только когда запросили все потребители, или делать буфферизацию, или стартовать с одного потребителя, а в остальные пропихивать данные оп push модели.

На самом деле в питоне это наверно действительно сделать трудно и имеет смысл только в том случае когда пареллелизация реально необходима. Так можно кешировать значения до форка и потом просто по очереди проводить анализ.
1) спасибо, вы правы, closure именно это, я перепутал. Я думал о сохранении значения внутри функции, чтобы можно было её возобновить после остановки. Это больше к reentrant.
3) да, я именно решил использовать push модель. Я не думал в терминах fork, для меня она больше относится к процессам.
Да, вопрос в целом был именно про то, как в случае pull модели разветвить поток данных. Не понятно, как можно генератор распараллелить на всех потребителей, полностью его не буферизуя и не заставляя пользователей писать очень сложный или непонятный код.
Как я понимаю, push и pull модели — из сетевой коммуникации? В обычном программировании есть термины для этого?
Разве у вас не pull модель:
s = Sequence(
    lambda i: pow(-1, i) * (2 * i + 1),
)
spi = Source(
    CountFrom(0),
    s,
    ISlice(10**6),
    lambda x: 4./x,
    Sum(),
)
results = list(spi())

вы ведь берете элемент из последней операции в spi, для этого она забирает элемент из предпоследней и т. д.
Что касается терминологии то она используется по разному, например аналог это git push/pull, системы сборки вытягивают (pull) зависимости (например так работает gradle анализируя какие задачи нужно решить для сборки проекта и рекурсивно подтягивая их), в kotlin есть понятие cold streams (https://kotlinlang.org/docs/reference/coroutines/flow.html). Мы такую терминологию применяем в dataforge, там можно делать как pull, так и push модели.
Да, у меня здесь pull модель, и она основная. Но для распараллеливания анализа — именно push модель.
Спасибо!

Ярослав, хорошая работа, но давай я поизображаю из себя оппонента и укажу на проблемные места.


В том, что касается конкурентов, список явно не избыточен, и начать, разумеется, надо было с https://spark.apache.org/ или с его питоновской производной pyspark. Собственно, все системы такого рода должны сравниваться именно с ним. Есть еще несколько аналогичных систем, в том числе с байндингами на питоне. Ну и в порядке знакомства, мог бы сослаться на https://doi.org/10.1051/epjconf/201817705003.


Ключевая проблема, которая не решается pipeline процессорами, и твоя система тут не исключение — это валидация промежуточных состояний. Собственно проблемы нет, если нет кэширования промежуточных результатов, но если ее нет, то ленивость оказывается не очень полезной.


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


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


Есть еще куча вопросов, но пока остановимся на этом.

Спасибо, Александр.

Я не понял, как связаны валидация с кэшированием. Кэширование — это вспомогательная вещь для производительности, которая никак не связана с правильностью передаваемых в кэш данных. Он должен работать (почти) при любых данных.

В моём опыте анализа данных проблема валидации типов не возникала. Человек может написать х вместо х+1, и никакая валидация типов здесь не поможет. Строка вместо числа питоном отловится. Поэтому да, нужно внимательно следить за своим анализом. В своём опыте я пришёл к выводу, что высокоуровневый язык гораздо больше подходит для сложных алгоритмов анализа данных, чем С++ — это моё предпочтение.

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

Я не понял большого отличия 2 и 1й «проблем», но 3я интересная и по делу.
Да, нужно работать также с неоднородными элементами, и фреймворк это делает (скорее в других частях раскрою другие примеры, но в данном примере Writer берёт только те значения, которые являются строками текста, и где в контексте есть output, а другие он пропускает далее, ничего с ними не делая).
Калибровки с данными я как раз анализирую фреймворком. Да, надо иметь несколько потоков данных. Это возможно, нужно действительно смешивать их в нужном месте. Глобальные переменные и состояния, к счастью, не требуются, всё в рамках этих потоков. Состояние элементов — так или иначе, конечно, есть (нужно же где-то хранить настройки и т.п.).

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

Вопросы про типы — это вопросы не про фреймворк, а про язык. Фреймворк не стремится быть лучше Питона. Что возможно — отлавливается в начале (при инициализации последовательностей). Питон — очень популярный язык, и лично мне он очень нравится, он простой, его синтаксис позволяет мне думать не о коде (где какой тип переменных объявлять), а об алгоритме. Его возможностей достаточно для моего анализа, поэтому я выбрал его. Гибкость и простота, скорость разработки в моих задачах важнее производительности.
Для начала кэширование. Если у тебя есть кэш, он может протухнуть. Ты что-то поменял в программе и ожидаемый результат поменялся. Если ты не инвалидировал кэш, получаешь ошибку. Валидация кэшей — это вообще глобальная проблема и у нее нет простых решений.

Теперь про типы. Есть разные уровне проверки данных. Первый уровень проверки — это структуры — это у тебя есть, ты проверяешь, что к тебе прилетели твои Sequence, а не что-то левое. Следующий уровень — это проверка типов содержимого, если твоя задача берет таблицу, она должна проверить, что это таблица, а не число. Вот этого уже нет. И наконец, твои x и x+1 — это проверка состоятельности данных. Как правило это уже делается в момент выполнения, хотя в супер-типизированных языках вроде Хаскеля, можно частично перенести это на уровень типов.

Фреймворк не стремится быть лучше Питона

Это точно. Но я сильно не уверен, что подобные системы можно всерьез делать на питоне. Минимальные штуки и прототипы — возможно.

Если у меня есть один кэш (что актуально при разработке), то можно контролировать, чтобы он удалялся, но можно и автоматизировать его удаление.
Если это глобальная проблема, то к фреймворку она не относится.
Django написана на питоне. На С++ она была бы бесполезна, потому что он не удобен для веб-программирования (как и для высокоуровневого анализа данных).
Более того, множество вещей нельзя сделать на низкоуровневом языке. Там даже словаря с разными типами данных (о чём ты писал) зачастую нет.
"И наконец, твои x и x+1 — это проверка состоятельности данных. Как правило это уже делается в момент выполнения" — нет такого. Программа делает то, что ты ей напишешь. Надо самому следить за своим анализом в любом случае.

1) не вижу в Apache Spark ничего, что связано с проблемами в моём анализе данных. Ни одного слова архитектура на сайте. Kubernetes, hadoop — мне никогда не требовались, как и все другие вещи, на которые ориентируется эта система.
Задача Lena — высокоуровневый анализ данных, а здесь всё ориентировано на распределенные вычисления, специфические базы данных. Больше похоже на библиотеку, чем на фреймворк.
Какие конкретно аналогичные системы есть? Я нашёл только одну на Питоне. Есть программы, которые называют себя фреймворками, но по сути они библиотеки, они не говорят пользователю как ему или ей писать программу.


Насчёт твоей статьи — спасибо за ссылку.
У неё тоже другие задачи.


The aim of the DataForge is to automate some standard tasks like parallel data processing, logging, output sorting and distributed computing.


И ещё: я не отправлял эту статью в хаб Big Data, потому что здесь нет никакой специфики больших данных. Это прежде всего архитектура для высокоуровневого сложного анализа, когда сложности в самой архитектуре, сведении разных данных вместе. Скорость в ней не в приоритете.

У меня есть данные. Мне их надо анализировать. И… как мне поможет в этом Lena?
Я физик, а не программист. У меня есть куча файлов с которыми я вожусь в Origin, TableCurve, Excel, сейчас еще и Pandas задействовали.
Очень хочется гуманитарную составляющую — вот нейтрино пролетело туда — сгенерило файл состоящий из таких-то строк/данных/..., вот детектор сработал так — сгенерил то-то. Получить нам надо то-то и то-то. А теперь мы всё это обрабатываем смотрите как.
А так получается в статье информация только для программистов, а программистам-то не нужно обрабатывать данные — им нужно программы писать.
Ну так возьмите ROOT или какую-нибудь монструозную поделку на основе него. Тут надо все-таки разделять фреймворк и модули к нему для работы с конкретной задачей. Универсального тут ничего нет.
возьмите ROOT
спасибо, тот случай, что чтобы загуглить — надо знать, что это существует. Нашел.
Если что, это шутка была. Я его никому не рекомендую брать без острой необходимости, но в физике частиц без него никто не работает. Я не очень понял, как вы можете о нем не знать, если из этой области.
Excell из серьезного анализа кстати предлагаю выкинуть (для построить график на коленке во время сеанса — нормально), как впрочем и Origin. Origin — это мощная система, но работает только если это одноразовый анализ. Для того, чтобы второй раз сделать то же самое, надо сделать примерно столько же работы, как в первый.
Origin — это мощная система, но работает только если это одноразовый анализ.

Так так и получается. Изменили качественно что-то в эксперименте — структура исходных данных поменялось. И так постоянно. Единственное исключение — у меня лежит порядка 200 файлов с более-менее однородными данными, в Origin я попробовал, умучался и стал возиться с питоном.
Мы перешли на питон для визуализации пару лет назад по этой причине. Сейчас постепенно подтягиваем инструментарий на Kotlin, но пока наверное его не стоит использовать если анализ не на JVM.
А можете показать пример исходных данных, и что из них нужно вытащить? Интересно посмотреть, как физики работают, какие визуализации используют, с какими проблемами сталкиваются, если есть такая возможность (можно ссылку на публикацию какую, для общего понимания).
Да можно конечно.
Вот пример.
Файл вида:
x1 y1
x2 y3
……
Это зависимость скорости от координаты при данном токе и данном отношении неких радиусов (геометрия системы).
Надо:
1) Построить зависимость скорости а) максимальной б), в какой-нибудь координате от тока для каждого отношения радиусов.
2) Аппроксимировать функцией содержащей линейную часть и явно нелинейную с неким параметром.
3) Построить зависимость этого параметра (характеризующего нелинейность) от отношения радиусов.
Так-то просто, но значение тока — в имени файла, отношение радиусов — в имени папки, и т. п…
Ссылку отправил в личку.
Это магнитная гидродинамика. А у вас всё как-то очень круто.
Физики — это понятие растяжимое. Если говорить о физике частиц, то почти повсеместно используется вот эта система: root.cern.ch. Данные к сожалению в ней же. Документацию по формату можете искать долго и читать ее еще дольше, но разобраться там невозможно. Достаточно сказать, что там используется самодельная квази-рефлексия на С++ с самодельным же форматом сериализации как Java serialization, только на С++ и с кучей граблей и костылей. В общем, никто кроме самого рута, читать этот формат не может (ну еще немного умеет вот эта штука: github.com/scikit-hep/uproot, но далеко не все).

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

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

По визуализации как правило используют тот же Рут — он исходно создавался для построения гистограм. Но к счастью в последнее время начали переползать на питон. Мы на эксперименте используем в процессе сбора свою собственную систему на JFreeChart (с большим количеством настроек). Для статей использовали Origin, но сейчас переползли на Plotly.

С публикациями туго, поскольку у физиков не принято говорить о софте. Это такая магия, которая или работает или не работает. Сейчас ситуация начала меняться и физики хотя бы начали признавать факт того, что software matters, но очень медленно.

Если вопрос про котлин, то вот статья: medium.com/@altavir/plotly-kt-a-dynamic-approach-to-visualization-in-kotlin-38e4feaf61f7, а вот светлое будущее: kotlinlang.org/docs/reference/data-science-overview.html

Lena не читает данные. Но вы можете с ней свести вместе разные данные (читаемые разными элементами).
Для нормальной обработки нужно в какой-то момент их унифицировать.
Если проблем с архитектурой, сведение своих данных (внутри вашей программы) вместе у вас не возникало — то вряд ли фреймворк нужен.
Анализ данных — это описание программ, поэтому я не вижу здесь большого отличия от программистов. Всё, что можно автоматизировать — нужно автоматизировать.

Я думал ещё над вашим вопросом. Я думаю, что Lena как раз очень поможет в таком анализе. Программы в любом случае придётся писать, и насколько они соответствуют физическим процессам — это полностью зависит от того, как вы их напишете.
Что касается Lena — если вам нужно использовать данные из кучи файлов, то контекст как раз сводит эти данные воедино. Это очень удобно, иначе пришлось бы вручную всё это сводить, это тяжело и не продуктивно.

Использует ли ваш фреймворк numpy/pandas/dask — уже готовые, широко распространенные реализацию быстрых массивов и операций над ними?


По упоминан PyPy думаю, что нет, и это крайне плохо.

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

Зачем смотреть код, если проще спросить)
Один из плюсов "не использования" чего-то — нет зависимостей. На некоторые системы может быть невозможно установить ту или иную программу, в особенности большую. Поэтому архитектурный фреймворк в идеале должен работать для максимального числа систем. Структурная подтипизация позволяет пользоваться элементами даже без фреймворка: можно вызывать метод run и не надо ничего наследовать и т.п.
Кстати, у меня есть Numpy Histogram в подпапке structures, вместе с обычной гистограммой Lena. Я им не пользуюсь, правда, и к архитектуре она имеет слабое отношение, просто один из возможных элементов.

Вам нужны быстрые массивы? Используйте numpy. Нужен быстрый код? PyPy. PyPy не плох, у него есть свои приложения.


Быстрый массив — это не вопрос архитектурнэы, а локальная вещь в вашей функции или классе…


Массивы numpy хранятся целиком в памяти, поэтому подходят не для всех задач.


Архитектура — это общая вещь. Если надо, Вы используете PyPy, но можете также использовать numpy.

Ну PyPy все таки не настолько быстрый как numpy или библиотеки с плюсовыми ядрами. Здесь скорее актуальнее реализовывать обрабатывающие функции с использованием numba.
Вполне возможно, что не такой быстрый. Есть случаи, когда быстрые массивы не нужны или алгоритм нельзя переписать в их терминах. Думаю, у PyPy есть свои приложения. Про Numba написано, что он только часть языка Python поддерживает (PyPy, насколько я помню, тоже). В любом случае я не вижу смысла ориентировать архитектуру на конкретные классы.
На самом деле ленивые вычисления с numpy не совместимы (он не по одному значению обычно обрабатывает, а целый массив), и если у вас все вычисления — последовательность преобразований массивов, то нужно либо целиком передавать эти массивы между элементами (хотя почему бы и нет), либо не пользоваться этим фреймворком.
Мне ленивые вычисления, которые сберегают память (и частично скорость, т.к. не надо ждать конца чтения данных для начала работы) показались важнее ориентации на быстрые массивы (я сам ими не пользуюсь, и проблемы у меня больше возникали архитектурного плана, как передать значения от одного элемента к другому).
Sign up to leave a comment.

Articles