Pull to refresh
13
0
Александр Питасов @Dartya

User

Send message

Откуда у вас "большое число параллельных потоков" в машин лёрнинге? WebFlux используется, когда речь идёт о десятках тысяч одновременных соединений

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

Вы с тем же успехом могли бы задать вопрос о необходимости распределенной среды, Kubernetes и, собственно, Java, если все можно делать проще на питоне, и для работы используется только локальная среда. Надеюсь, ответил на этот вопрос и подкинул идей для новых.

Как веб API может повлиять на скорость мне не понятно

А мне не понятно, почему Вы делаете привязку к WEB API, если WebFlux - это в первую очередь фреймворк на основе Project Reactor, по умолчанию использующий Netty. Условный пример, который в жизни не применим, но как эксперимент сойдет: сделайте Flux, пусть генерирует в лог сообщения о положении луны и солнца в зависимости от системного времени, делает на основе положения солнца еще какие-нибудь расчеты (придумайте сами), и выводит в лог. Flux есть, реактивная цепочка есть, вывод в лог какой-никакой есть - где здесь WEB API? Затем можете тем же Flux'ом генерировать задачи для спарка.

Возможно, стоит освежить в памяти принцип работы и архитектуру Spark.

Чтобы сократить комментарий, предлагаю ознакомиться со следующими материалами:

https://spark.apache.org/docs/latest/cluster-overview.html, https://blog.knoldus.com/understanding-the-working-of-spark-driver-and-executor/

Таким образом получается, что у Spark Driver довольно много сетевых взаимодействий с исполнителями. Учитывая, что как минимум логика приложения выполняется асинхронно и в реактивных цепочках, осмелюсь предположить, что именно по этой причине WebFlux наиболее эффективен в части потребления аппаратных ресурсов и скорости выполнения задач.

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

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

А если число "одновременных соединений", как Вы говорите, (или RPS) будет исчисляться не десяткамми тысяч, а тысячами, скажем, условными 9000, - все, WebFlux не используется? Можно ссылку на авторитетные источники, исследования, возможно Ваши публикации, или, выражаясь Вашим языком, откуда вы взяли, что WebFlux используется только когда речь идет о десятках тысяч одновременных соединений?

WebFlux - более сложный API на его поддежку требуется больше рессурсов.

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

Если у вас появляются подозрительные результаты, то вы должны исследовать этот вопрос...

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

И не смотря на это, вы нашли время чтобы на него переписать. А теперь пишете, что не нашли времени на ответ на вопрос: почему всё таки проседает производительность.

Касательно тона Ваших комментариев - я прошу Вас его сменить, так как Вы мне не заказчик, не руководитель, не мать, не сестра и не любовница, чтобы критиковать меня за потраченное мое личное время, а так же задавать вопросы в духе "Это где вы такое взяли?", "Откуда у вас "большое число параллельных потоков" в машин лёрнинге?", и пр. Я Вам ничего не должен, и Ваши хотелки можете оставить при себе, как я Вам выше писал. В противном случае можете продолжать пытаться самоутверждаться в комментариях сколько угодно, я не буду на Вас реагировать. Судя по Вашему профилю, Вы именно за этим и приходите на Хабр. Призываю использовать корректные выражения в своей речи в дискуссии со мной.

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

...

Моя гипотеза - что Spring Flux будет иметь такую же производительность как и Spring MVC. Чтобы точно ответить причину тормозов - надо профайлить. Bottleneck - он вcегда в неожиданном месте.

Я Вас услышал, блестящая гипотеза, спасибо. Если еще что-то надумаете, пишите, с удовольствием почитаем.

зачем его предотвращать? [stop the world - прим.]

Слишком толсто. Погуглите этот вопрос, уверен, найдете много интересных материалов.

У вас точно своп отключен?

Отключен. И, если уж вы вспомнили про Win10Pro, основываясь на Ваших рассуждениях выше и предвидя Ваши вопросы - да, перезагружал, а вот переустановить не пробовал.

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

Если у вас kubernetes, почему бы не протестировать там

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

---

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

Это где вы такое взяли? Обычно подразумевается что код в потоках быстрее с точки зрения throughput, но зато реактивный может быть выгоднее с точки зрения latency.

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

Другой вопрос, зачем это в данном приложении, которое что-то абстрактно обучает, что никому не нужно? Видите ли, я, когда начинал этот проект, одним из требований ставил горизонтальную масштабируемость системы, и во многом система держится на Spark и Kubernetes (и даже ускоритель Rapids для Spark Jobs на GPU имеется). Так как частью системы является приложение на Java, и так уж случилось, что для обеспечения многих вспомогательных механизмов использован Spring, использовать WebFlux и сравнить его с сервлетным стеком было естественным желанием.

Кроме того, непонятно какое вообще влияние может оказывать веб-API для обучения на основе GPU ???

Я где-то в статье сказал, что оказывает, или что Spring в целом и WebFlux в частности - это только про WEB API? Опять же, результаты перед Вами: перешел на реактивный стек - управление ресурсами стало более эффективным. Почему? Извините, я не нашел 100% ответа на этот вопрос с доказательствами из профайлера и хипдампов, а потратить еще несколько месяцев личного времени на поиск ответов в данный момент не готов. Есть результат, результат выражен в графиках, таблицах, описана методика тестирования - результатом поделился.

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

Зачем там вообще нужно веб-API?

Как интерфейс взаимодействия с внешним миром, предоставляющим возможность запуска Spark Jobs. С таким же успехом можно было сделать и чтение из очереди или запуск задач по расписанию, по получению SMS или e-mail, и др. Так как у меня нет заказчика с его персональными хотелками, я выбрал такой способ взаимодействия приложения с внешним миром.

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

Хотелось бы видеть, скажем, "реактивный API кассандры быстрее нереактивного на таких-то запросах на 15% потому что это, это и это". Ваше же измерение абстрактного Machine Learning-а в вакууме вообще ни о чём не говорит

В этом частично соглашусь, частично нет. Признаюсь, частью с профайлингом я менее всего доволен в данной статье, так как не удалось привести сервлетную версию приложения к подобным результатам в части потребления ресурсов, как у реактивной версии, и не удалось найти ответы на вопросы о причинах такого поведения. Ограничение памяти хипа, времени работы GC и замена G1 на Z - наверное, сравни прикладыванию подорожника, но этого оказалось достаточно, чтобы улучшить работу с ресурсами и предотвратить stop the world (кстати, я не сказал, что стопы оказывают существенное влияние на результаты работы, и заметил их только при подключении профайлером).

Стоило ли "изолировать" логические модули и отдельно их тестировать и профилировать - вопрос постановки задачи, я такую перед собой не ставил. Почему не ставил - потому что материала и без того наработано на 30+ страниц А4, и по итогу работ имеется результат достаточный для того, чтобы его показать, что я и сделал в выводе.

Что касается "абстрактного Machine Learning-а в вакууме" - во всех трех статьях по этой теме я не претендовал на то, чтобы разработать новый высокоэффективный алгоритм, новую технологию, библиотеку или фреймворк для Java, и все иное в этом духе - я не обладаю такими компетенциями. Изначально мне было интересно, а есть ли альтернатива Python и Dask в мире ML такая, чтобы за основу можно было взять Java с ее существующими библиотеками и фреймворками ML, NN, и прочих; взять оркестратор контейнеров и запустить параллельное обучение на нескольких GPU. Вот с этим, я считаю, что справился в полной мере - альтернатива есть, и она работает, что бы Вы ни говорили. Насколько она эффективна - большой вопрос, который я не готов раскрывать ни в рамках опубликованной статьи, ни в рамках данного комментария. Но, исходя из имеющегося опыта уже могу сказать, что инкрементальное обучение XGBoost в библиотеке Java не поддерживается, а вот при ресерче для Python я находил пример с работающими параметрами.

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

Есть гипотеза, что решить может, но надо много ресерча и тестов. Есть множество условностей: начиная от того, что в таких системах нужно подбирать период данных для обучения, и период, на который делать предикт - он, обычно, на несколько (1-3) периодов вперед делается, если делать вывод из тех статей, которые мне удалось найти.

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

Спасибо за комментарий :)

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

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

Вот только если extraClassPath попробовать, так как при старте драйвер отправляет джарник с классом на воркер, иначе бы джоба не поехала; и в логах экзекутора соответствующая запись имеется:

INFO Utils: Fetching spark://172.17.0.6:33139/jars/service.jar to /tmp/spark-cc20e07c-527f-416a-95a7-479a902201fe/executor-04e6f504-1930-43ac-a872-8a7202e64589/spark-c043a020-cc10-410c-8988-371a1c33ffe4/fetchFileTemp8189536983640938756.tmp

Вернусь с результатами так же в коменты, спасибо!

Дело обстоит следующим образом:

Подключение Kryo в случае с Rapids:

.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") 
.set("spark.kryo.registrator", "com.nvidia.spark.rapids.GpuKryoRegistrator")
.set("spark.kryo.registrationRequired", "true")
.registerKryoClasses(new Class<?>[]{LongValue.class})

Вызываем метод http://localhost:9090/gpu_test, получаем:

org.apache.spark.SparkException: Job aborted due to stage failure: Failed to serialize task 0, not attempting to retry it. Exception during serialization: java.io.IOException: java.lang.IllegalArgumentException: Class is not registered: org.apache.spark.sql.catalyst.InternalRow
Note: To register this class use: kryo.register(org.apache.spark.sql.catalyst.InternalRow.class);

Предвидя следующую ошибку, сразу указываем InternalRow[]:

.registerKryoClasses(new Class<?>[]{InternalRow.class, InternalRow[].class, LongValue.class})

При вызове http://localhost:9090/gpu_test получаем:

Request processing failed; nested exception is org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 6) (172.17.0.5 executor 0): java.lang.IllegalStateException: unread block data

В логах экзекутора:

22/08/01 17:16:10 ERROR Utils: Exception encountered
org.apache.spark.SparkException: Failed to register classes with Kryo
...
Caused by: java.lang.ClassNotFoundException: com.mlwebservice.model.LongValue
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)

Решение пока не нашел.

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

Драйверы для всех GPU имеются для линукса. У меня есть доступ только к виндовым машинам, линукс накатить нет возможности, а VMWare и VirtualBox не позволяют проводить такие манипуляции с GPU, остался вариант только с WSL.GPU в наличии по одному на каждой машине: 2060, 1650; с возможностью добавить на одну из машин 1080 Ti.

Вторая причина кроется в варианте использования той же DJL без Spark, но на GPU.

Третья причина (сейчас будет дилетантское суждение, так как изучил вопрос лишь поверхностно) - образ воркера для Kubernetes Operator должен содержать необходимый JVM, либы и скрипт обнаружения ресурсов, то есть все равно пришлось бы делать образ.

А так - да, Standalone кластер в докере - оверхэд и не нужно, для Yarn-Mesos слишком мало машин и ресурсов, а до кубера на винде я пока еще не дошел, и вряд ли дойду :)

Information

Rating
Does not participate
Registered
Activity