Comments 41
А по какой причине шейдеры в GLSL кроспилируются каждый раз в рантайме заново, а не заранее на этапе сборки и не бандлятся в игру/приложение собственно в виде GLSL?
+1
Конечно, в случае медленного/тяжёлого транслятора они транслируются заранее. Неудобства будут в основном у разработчиков и моддеров, но даже им можно помочь кешируя результат в файловой системе и транслируя только по необходимости. Правда дистрибуция стороннего парсера это ещё отдельный вопрос.
Т.е. всё решаемо, но мне было интересно откуда же берутся эти цифры, ведь тут 3 секунды старта, там 30 мегабайт исходников, так проекты и становятся неповоротливыми.
К тому же, я тут рассматриваю абстрактный парсер, у которого применением может быть не только трансляция шейдеров, но и, к примеру, скриптование.
Т.е. всё решаемо, но мне было интересно откуда же берутся эти цифры, ведь тут 3 секунды старта, там 30 мегабайт исходников, так проекты и становятся неповоротливыми.
К тому же, я тут рассматриваю абстрактный парсер, у которого применением может быть не только трансляция шейдеров, но и, к примеру, скриптование.
0
Тогда ещё вопрос — основной платформой и для текущего кроспилятора и для «к примеру, скриптования» является Android? Почему именно Java (как платформа в первую очередь, JVM)? Понятно, что кроспилированные шейдеры можно использовать в сыром виде, но что со скриптованием? Рассматривали ли вы какие-то альтернативные варианты типа скриптового рантайма на С например?
0
1) 21-ый век на дворе, а JMH почему-то не пахнет. Как так? Набросал замер на JMH — получается, что IntelliJ парсер в 4 раза быстрее чем JavaCC.
2) GaugeKotlinIntellijParser зачем-то пересоздаёт окружение при каждом парсинге. Зачем так? Его же переиспользовать можно и нужно.
3) yk.jcommon.utils.IO#readFile(java.lang.String) не закрывает файл
2) GaugeKotlinIntellijParser зачем-то пересоздаёт окружение при каждом парсинге. Зачем так? Его же переиспользовать можно и нужно.
3) yk.jcommon.utils.IO#readFile(java.lang.String) не закрывает файл
+3
Мне кажется так как претензия конкретно к холодному старту, JMH тут не имеет смысла использовать.
+3
Если бы речь шла о скорости работы SQL запроса в enterprise приложении, то, да, её можно и без JMH измерить. А тут наоборот: JMH проще, быстрее и точнее.
1) Вообще говоря, «производительность» автор объявляет первым требованием
2) В JMH без проблем можно измерять «холодный старт». И измерять это можно более полно, т.к. в JMH есть встроенный механизм Forks — будут запускаться отдельные Java машины
3) Код на JMH гораздо компактнее, чем «рукописный». В итоге, человеку со стороны проверить гораздо проще
4) В JMH встроено много разнообразных профайлеров. Автор приводит цифры без какого-либо анализа того *почему* цифры именно такие
1) Вообще говоря, «производительность» автор объявляет первым требованием
2) В JMH без проблем можно измерять «холодный старт». И измерять это можно более полно, т.к. в JMH есть встроенный механизм Forks — будут запускаться отдельные Java машины
3) Код на JMH гораздо компактнее, чем «рукописный». В итоге, человеку со стороны проверить гораздо проще
4) В JMH встроено много разнообразных профайлеров. Автор приводит цифры без какого-либо анализа того *почему* цифры именно такие
0
Про SQL — это несколько пренебрежительно, не находите?
1. Первое требование — удобство разработчика и пользователя.
2. Тут я меряю конкретно то что мне нужно.
3. Компактнее не выйдет. При сохранении функционала, конечно.
4. Вы невнимательно читали.
5. Мне не нужна цифра «время исполнения в Х итерациях после У разогревов». Мне нужно минимальное время к первому, десятому, сотому, тысячному вызову.
6. На 1000й вызов JetBrains моя цифра выходит чуть больше чем в JMH, на 50000й — меньше. Как я указывал, при таком агрессивном JIT, меня интересует именно динамика. А конкретное число — вообще имеет мало смысла. Я бы ещё понял претензию об однообразии данных!
Ну и мелочи. Мои тесты проходят за пару секунд, а не за минуту, ещё и форматируется это всё, чтобы в любой момент можно было повторить замеры и скопипастить в нужный отчёт.
Думаю, в 21м веке каждый волен использовать любой удобный инструмент, если понимает что он делает.
1. Первое требование — удобство разработчика и пользователя.
2. Тут я меряю конкретно то что мне нужно.
3. Компактнее не выйдет. При сохранении функционала, конечно.
4. Вы невнимательно читали.
5. Мне не нужна цифра «время исполнения в Х итерациях после У разогревов». Мне нужно минимальное время к первому, десятому, сотому, тысячному вызову.
6. На 1000й вызов JetBrains моя цифра выходит чуть больше чем в JMH, на 50000й — меньше. Как я указывал, при таком агрессивном JIT, меня интересует именно динамика. А конкретное число — вообще имеет мало смысла. Я бы ещё понял претензию об однообразии данных!
Ну и мелочи. Мои тесты проходят за пару секунд, а не за минуту, ещё и форматируется это всё, чтобы в любой момент можно было повторить замеры и скопипастить в нужный отчёт.
Думаю, в 21м веке каждый волен использовать любой удобный инструмент, если понимает что он делает.
+1
1. JMH интересен, конечно, и иногда цифры даже совпадают на стотысячный вызов. А иногда мой стотысячный выходит быстрее. В общем, это совсем другая тема, и для статьи, возможно, надёжнее было бы выложить именно его результаты. Но в любом случае, это всего лишь тысячный вызов, а, как я написал — меня интересовала именно динамика времени.
2. Отличное замечание! Действительно, кешируя именно это значение можно ускорить JetBrains в два с половиной раза (в данных конкретных условиях).
3. Файл закроется в finalize, но вы правы, лучше делать это явно. Тем более раз он начал использоваться более чем для чтения пары файлов.
4. А вот почему в вашем замере ускорение в четыре раза — это действительно интересно. У вас используется CoreLocalFileSystem и именно он даёт буст. С первых же вызовов 0.4мс вместо 5. Уж не кешируется ли там результат парсинга?
2. Отличное замечание! Действительно, кешируя именно это значение можно ускорить JetBrains в два с половиной раза (в данных конкретных условиях).
Kotlin IntelliJ parser (without analyzer, cached project)
first call elapsed : 1272.145ms
min time in next 10 calls: 5.031ms
min time in next 100 calls: 1.486ms
min time in next 1000 calls: 0.38ms
Whole time for 1111 calls: 1.518542s
3. Файл закроется в finalize, но вы правы, лучше делать это явно. Тем более раз он начал использоваться более чем для чтения пары файлов.
4. А вот почему в вашем замере ускорение в четыре раза — это действительно интересно. У вас используется CoreLocalFileSystem и именно он даёт буст. С первых же вызовов 0.4мс вместо 5. Уж не кешируется ли там результат парсинга?
Kotlin IntelliJ parser (without analyzer, CoreLocalFileSystem)
first call elapsed : 1233.702ms
min time in next 10 calls: 0.418ms
min time in next 100 calls: 0.127ms
min time in next 1000 calls: 0.041ms
Whole time for 1111 calls: 0.11175224s
0
В №1 вы говорите об изменившихся цифрах. Но вы замеряете ДРУГУЮ имплементацию, а пишите что только заменили методику сбора.
Нужно быть внимательнее в построении предложений.
Нужно быть внимательнее в построении предложений.
0
Я правильно понял, что посмотреть можно только на парсер, транслятор закрыт? Что же касается обсуждаемого вопроса: мне кажется, что тащить компилятор в продакшен рантайм занятие достаточно бессмысленное, ничто не мешает скомпилировать заранее, а для разработки можно и размер увеличить и подождать немного один раз.
0
а как же скрипты в runtime запускать?
Вот пример проекта: https://github.com/s1monw1/KtsRunner
0
Да, можно не тащить. Но и о разработчике я бы не стал забывать тоже, он ведь тот, кто чаще чем кто либо другой будет запускать проект. Ну и habr.com/post/433000/#comment_19503612
0
Стоит попробовать преобразовать обычные правила в лево-рекурсивные в Kotlin грамматике. Скорее всего это ускорит парсер.
0
+ потестить профайлером на предмет неоднозначностей (ambiguity) в правилах. Грамматики из antlr репозитория часто ими грешат.
0
Да уже: обобщенный алгоритм LL(*) (а точнее ALL) хоть и упрощает написание произвольных грамматик, однако усложняет сам код парсера, ресолвинг неоднозначностей. Поэтому нужно аккуратно их писать. К тому же он динамический.
0
Также автору нужно попробовать использовать двухэтапную методику парсинга как это описано, например, в моей статье: Максимизация скорости парсинга в ANTLR.
0
Спасибо за ссылку, обязательно почитаю! Интересно будет узнать что в ANTLR можно сделать лучше. Однако, первой же в голову приходит мысль — а ведь в JavaCC я писал «в лоб», без каких либо ухищрений. И размер fat jar со временем первого старта — вряд ли изменятся.
(Кстати, грамматика на ANTLR — не моя)
(Кстати, грамматика на ANTLR — не моя)
0
А почему размер jar так важен? И у меня намного меньше:
- C# Optimized: 307 Кб (бинарь Kotlin.dll) + 1.48 Мб (размер рантайма).
- C# Standard: 305 Кб (бинарь Kotlin.dll) + 352 Кб (размер рантайма).
- Java: 671 Кб (скомпилированные файлы .class) + 330 Кб (размер рантайма).
Проверял с помощью моей тулы DAGE (пока что нестабильная версия, поэтому возможны баги).
0
Я не говорю что прямо так важен, но обращать внимание на него стоит, ведь даже на десктопе — это не просто 30МБ на терабайтном винте, это 30МБ кода.
(Плюс абзац из публикации, не буду повторяться).
Размер я мерял со всеми зависимостями.
kotlinAntlr-0.1-SNAPSHOT-jar-with-dependencies — 15МБ, основной вес в зависимости com.ibm.icu
(Плюс абзац из публикации, не буду повторяться).
Размер я мерял со всеми зависимостями.
kotlinAntlr-0.1-SNAPSHOT-jar-with-dependencies — 15МБ, основной вес в зависимости com.ibm.icu
+1
1) А зачем в зависимостях указывать
2) Зачем в jar-with-dependencies включать junit и hamcrest, ведь они только для тестов нужны? Зачем в измеряемый jar включать log4j и commons-lang3, которые вообще не используются в коде? Измерять нужно не jar-with-dependencies, а результат работы maven-shade-plugin'а. Либо оставляйте только реально нужные зависимости. Если всю эту ерунду удалить, то получается где-то 1 мегабайт.
3) Если удалить и yk/jcommon, то получается похоже на результат KvanTTT
<artifactId>antlr4</artifactId>
, когда во время выполнения достаточно <artifactId>antlr4-runtime</artifactId>
?2) Зачем в jar-with-dependencies включать junit и hamcrest, ведь они только для тестов нужны? Зачем в измеряемый jar включать log4j и commons-lang3, которые вообще не используются в коде? Измерять нужно не jar-with-dependencies, а результат работы maven-shade-plugin'а. Либо оставляйте только реально нужные зависимости. Если всю эту ерунду удалить, то получается где-то 1 мегабайт.
3) Если удалить и yk/jcommon, то получается похоже на результат KvanTTT
0
Спасибо, поправил
0
Стоит упомянуть, что теперь большую часть занимает не ANTLR и не парсер, а yk.yast:common:jar и yk:jcommon:jar
0
Это же относительные результаты, понятно что во всех случаях что-то дополнительное есть. Но если таки заморочиться, то JavaCC выйдет ещё красивее — 66КБ против ANTLR 680КБ. О чём я, кстати, упомянул.
0
Ещё раз: проблема не в конкретных цифрах, а в том, что они означают совсем не то, что читатель предполагает. Вы пишете, что «парсер на ANTLR занимает столько-то», и логично предполагать, что там «парсер+необходимая ANTLR обвязка».
А по факту же, вы туда вкладываете свои commons (которые занимают половину результитрующего jar), log4j, commons-lang и далее по списку.
А по факту же, вы туда вкладываете свои commons (которые занимают половину результитрующего jar), log4j, commons-lang и далее по списку.
0
Я нигде не говорил что это размер чистого парсера. Написано: "… посмотреть на размер джара со всеми зависимостями".
Кроме того, я везде привожу ссылки на код, который тестируется. Как тут можно что-то не так понять?
И ещё раз. Тем не менее. Я упомянул. Про чистый размер парсеров.
Кроме того, я везде привожу ссылки на код, который тестируется. Как тут можно что-то не так понять?
И ещё раз. Тем не менее. Я упомянул. Про чистый размер парсеров.
0
Как тут можно что-то не так понять?
Ясно как: никому и в голову не придёт, что вы, помимо необходимого, в jar включаете десяток-другой абсолютно лишних зависимостей.
0
И ещё раз. Тем не менее. Я упомянул. Про чистый размер парсеров.
0
На JavaCC парсера не нашлось, а вот на хайповом ANTLR, ожидаемо, есть (раз,
два).
Вообще говоря это одинаковые грамматики. Ну и ANTLR не такой уж и хайповый — там один мэинтейнер, который мержит пулл-риквесты раз в полгода. А версии выходят раз в год.
0
JavaCC парсер пока содержит не всю грамматику
А какой процент unit-test'ов (я про официальные Kotlin тесты) проходит этот парсер?
Например, JavaCC парсер считает, что -789 относится к определению переменной
x
, хотя это, очевидно, не так:fun getResult() {
val x = 123 + 456
- 789
}
+1
Не запускал, конечно. Грамматика не покрыта же ещё.
Кстати, интересно было бы по ANTLR посмотреть. Там наткнулся что вот такой код
Кстати, интересно было бы по ANTLR посмотреть. Там наткнулся что вот такой код
SimpleClass().foo<String>("")
не парсится.0
К слову, в ANTLR грамматике какая-то самодеятельность: callExpression в официальной Kotlin грамматике отсутствует. Из-за подобного растёт неоднозначность, время парсинга и наверняка будут ошибки
0
осталось процентов 10. Но не похоже чтобы они могли повлиять на производительность
1) У вас не реализован парсинг
functionType
, а это довольно мутная часть языка (леворекурсивная и всё такое)2) У вас не реализован парсинг
functionLiteral
, а это тоже непростой синтаксис. Например, в конструкции class Child: Parent by delegateMethod { ... }
фигурные скобки могут быть как телом класса Child
, так и лямбдой-параметром к методу delegateMethod
В общем, предлагаю сначала пройти хотя бы те тесты, которые проходит ANTLR парсер, а потом уже делать какие-либо сравнения.
+2
Sign up to leave a comment.
Компилируем Kotlin: JetBrains VS ANTLR VS JavaCC