Отличная огромная работа. Честно снимаю шляпу! Но как всегда есть одно "но"... На 99% уверен что как всегда все это будет работать без возможности выключить. Я понимаю вы увлечены и вам кажется все будут рады вашим фишкам. Но это редко бывает так. Лично я в итоге ушел с 2ГИС к конкурентам. Хотя мне он на самом деле нравился. Классная резкая графика, в отличии от "материальной" склонности других. Но слишком много шума, прям раздражающего. Невозможно отключить эти черные баннеры с полосами, невозможно убрать эти красные шапки о скорости. Почему не подумаете о сценарии - я уже много-много лет знаю тут каждую кочку на дорогах и каждый своротик, все что мне нужно - данные о пробках и больше не моргайти мне ничем :)
Кислород, азот, водяной пар. Все то что внутренности ракеты захватили с собой с земли и все это замерзло при запуске (Союз-2 летает на жидком кислороде с -183 градусов) и позже в самом космосе.
Ну это все цена иммутабельности. Как бы она известна изначально. Единственное ее преимущество, которое кричат на каждом углу - вы обезьяны, все равно где то ошибетесь. Понятно что изменить пару байтов поля в памяти не идет ни в какое сравнение с созданием нового объекта. А еще отказ от геттеров и сеттеров и работа прямо с полями дает вполне ощутимый прирост. Но ява уже на столько утонула в трясине академических и корпоративных методологий что все равно свой код не сильно исправляет. Как ни старайся библиотеки все равно тонут в десятках и даже сотнях прокладок интерфейсов и всяких паттернов ради самих паттернов а не реальной необходимости. Что часто проще от нее совсем отказаться.
А знаете ли вы о том что то что принято называть JavaVM (где исполняется байт-код java) официально носит название JRE - Java Runtime Environmen. Или Python Runtime - тоже выполняет JIT, но никто его не называет PythonVM. Так что VM / Runtime по сути пустое маркетинговое словоблудие :)
Составная часть которая и выполняет любой код Dart. Что в виртуальной машине, предварительно транслируя его из байт-кода, что в нативной версии, только трансляция выполнилась раньше в процессе сборки. Смысл один - весь dart код всегда работает под управлением одной и той же внешней системы. Называйте ее как хотите VM / Runtime. Принцип функционирования от этого не меняется.
Пришлось сходить на официальный сайт чтобы поставить точку: dart compile exe A standalone, architecture-specific executable file containing the source code compiled to machine code and a small Dart runtime. Идем по ссылке что такое Dart runtime: On native platforms, the Dart runtime is automatically included inside self-contained executables, and is part of the Dart VM provided by the dart run command. Вот и весь рантайм - всего лишь обрезанная часть той самой DartVM. Без функций оптимизации и динамической загрузки.
Очевидно потому что asMap() всегда вернет Map<int, ЧтоТо> а этот метод возвращает Map<String, Object?>, т.е. трансформирует ["key1", value1, "key2", value2...] в {"key1": value1, "key2": value2 ...}
Я повторюсь еще раз - единственная разница между VM языка и Runtime только в том что с VM вы берете свой файл и без изменений его переносите между linux/windows/ЧемТоЕще. Или прибиваете гвоздями в свой готовый файл и собираете уже отдельно для linux/windows/ЧтоТоЕще. .Net runtime, Node.js - это тоже runtime только вот никакой разницы с JavaVM у них нет. И Dart runtime точно такой же, просто он включен в вашу программу и не требует установки отдельно.
Ну в итоге ответ на ".. что вроде бы должно давать максимальную производительность..." - по тестам Dart код проигрывает в производительности нативному C++ в 2-3 раза. Это согласно тестам в которых использовались оба языка: Dart vs C++
Конечно есть - накладные расходы. Собственно этот обмен мнениями и начался с цитаты про нативник dart: ".. он компилируется в бинарный код платформы – что вроде бы должно давать максимальную производительность.."
Вы говорите о том что можете сделать в нативе. В конце концов DartVM сама написана на C++ и при желании можно хоть повторить ее в своем коде. Но код Dart сам по себе никогда не может быть так запущен. Он оборачивается в task и встает в очередь EventLoop.
Но ведь JavaVM при работе постепенно перекомпелирует байткод в нативный (прогревается), спустя какое-то время вся система может быть уже в нативном виде. Получается VM плавно мутирует в рантайм? :)
В реальном нативе, тот же C/C++. Ваша программа набор инструкций процессору. Которые он будет выполнять без остановки, пока они не закончатся. И нет в собранном бинарнике никаких Loop. Все инструкции идут шаг за шагом без остановок. Ну только системный планировщик их приостанавливает, рулит их приоритетом. Ваш код Dart это не иснтрукции процессору а инструкции DartVM. Хоть они и уже в бинарном виде. Но запустит их именно DartVM в виде своих task/microtask. И он может менять их порядок выполнения для всяких await/thien.
Хотите "чистой архитектуры" - возьмите за правило никогда не писать во flutter коде ничего что не рисует картинку. Вообще ничего. Все что хоть что-то считает - отдельная библиотека на чистом dart. Это правило для себя я вывел на первой приложуньке. На второй правило дополнилось - все что считает хоть что-то обязано всегда инициироваться с Isolate.spawn(). Ничего считающего, получающего из сети, и вообще делающее хоть что-то не рисующее не может быть в основном изоляте с рендерингом. Основной изолят должен только получать что-то от других и соответсвенно реагировать на эти сообщения. Я не особый сторонник "чистой архитектуры" но с этими правилам что-то подобное получается само по себе.
Код ни в Dart ни в Java не запускается сам по себе. Его запускает JavaVM или DartVM. В случае Dart это тот самый Loop который строит очередь тасков/микротасков и построчно выполняет каждый шаг программы. Как не обзовите НативнаяКомпиляция/Предкомпиляция - все равно каждое действие проходит через очередь DartVM. Она рулит потоком управления.
Все деления Runtime/VM - чистый маркетинговый флуд. Ну или нужно ставить VM отдельно / она включена в ваш код.
Я собираю один единый exe с помощью GraalVM для десктоп на JavaFX. Приложение полностью независимо, один exe не требует никаких предустановленных компонентов на машине. Но это все та-же JavaVM только находится внутре того-же exe. Это теперь Runtime или VM?
Именно DartVM работает с память, обслуживает ваши изоляты (ведь main запускается в изоляте). Абсолютно то же самое делает и java. Только Java по ходу исполнения переводит байт код в нативные (прогрев) и в теории может что-то оптимизировать под конкретное железо. А Dart это сделает сразу на "усредненую" машину.
Минимальный exe "Hello world" который собирает Dart под Windows - 5Mb. Если бы он собирал несколько килобайт как это делают C/C++ можно было бы рассуждать о неком настоящем нативе.
Это же не Дарт как таковой работает, он компилируется в бинарный код платформы – что вроде бы должно давать максимальную производительность
Дарт просто собирает нативный образ DartVM + ваш код в виде натива, который может запустить система. А по сути это та же самая а-ля javaVM, только маленькая.
Не верно. setState по любому заставит перестроить нижележащее дерево виджетов
Зачем верить или нет. Нужно знать. Идем в исходники flutter и смотрим определение setState (отбросим assert, так как их все равно нет в рабочем приложении): void setState(VoidCallback fn) { final Object? result = fn() as dynamic; _element!.markNeedsBuild(); } Ну можем еще markNeedsBuild() проверить: void markNeedsBuild() { if (dirty) { return; } _dirty = true; owner!.scheduleBuildFor(this); } Ничего setState не перестраивает, только меняет булев флаг dirty.
Так что сколько его не жмакай хоть каждые 1 наносекунду перестройка будет не чаше чем раз на кадр. И в статье речь не про сам setState а что лучше использовать виджеты анимации. Но внезапно все анимационные виджеты наследуются от AnimatedWidget, который сам по себе StatefulWidget. И все анимации жмакают этот самый setState сразу после текущего кадра. Т.е. кадр отработался, они тут же считают значения на следующий кадр и банально дергают setState.
Особенно большие задержки могут проявляться при отрисовке длинных листов, состоящих из множества элементов.
А вот неправда, если вы только сами себе проблем не сделали. Листы по умолчанию заворачивают своих детей в RepaintBoundary. А это блокирует перерисовку вложенных виджетов. Поэтому при скролле дети не пересоздаются, хотя казалось бы дерево меняется. Только если вы следуете "лучшим практикам иммутабельности" - самой идиотской идиоме, и на каждое движение пересоздаете весь список нижележащих данных. Тогда да, убогая иммутабельность неизлечима :)
Я не очень понимаю что за проблема с "преобразованием и возвращаемым значением". Для каждого вызова можно сделать свои static функции которые будет получать или возвращать то что нужно. Но главное в этом всем одно - ваш подход это только если вы сами автор и натива и java кода. И можете сразу все править для точного соответствия классов и натива. Если ваш натив предполагает быть библиотекой для широкого использования или даже просто использоваться в нескольких собственных проктах то это породит кучу прослоек под каждый чих.
Отличная огромная работа. Честно снимаю шляпу!
Но как всегда есть одно "но"...
На 99% уверен что как всегда все это будет работать без возможности выключить.
Я понимаю вы увлечены и вам кажется все будут рады вашим фишкам.
Но это редко бывает так.
Лично я в итоге ушел с 2ГИС к конкурентам. Хотя мне он на самом деле нравился.
Классная резкая графика, в отличии от "материальной" склонности других.
Но слишком много шума, прям раздражающего. Невозможно отключить эти черные баннеры с полосами, невозможно убрать эти красные шапки о скорости.
Почему не подумаете о сценарии - я уже много-много лет знаю тут каждую кочку на дорогах и каждый своротик, все что мне нужно - данные о пробках и больше не моргайти мне ничем :)
Кислород, азот, водяной пар. Все то что внутренности ракеты захватили с собой с земли и все это замерзло при запуске (Союз-2 летает на жидком кислороде с -183 градусов) и позже в самом космосе.
Ну это все цена иммутабельности. Как бы она известна изначально. Единственное ее преимущество, которое кричат на каждом углу - вы обезьяны, все равно где то ошибетесь. Понятно что изменить пару байтов поля в памяти не идет ни в какое сравнение с созданием нового объекта. А еще отказ от геттеров и сеттеров и работа прямо с полями дает вполне ощутимый прирост. Но ява уже на столько утонула в трясине академических и корпоративных методологий что все равно свой код не сильно исправляет. Как ни старайся библиотеки все равно тонут в десятках и даже сотнях прокладок интерфейсов и всяких паттернов ради самих паттернов а не реальной необходимости. Что часто проще от нее совсем отказаться.
А знаете ли вы о том что то что принято называть JavaVM (где исполняется байт-код java) официально носит название JRE - Java Runtime Environmen.
Или Python Runtime - тоже выполняет JIT, но никто его не называет PythonVM.
Так что VM / Runtime по сути пустое маркетинговое словоблудие :)
Составная часть которая и выполняет любой код Dart. Что в виртуальной машине, предварительно транслируя его из байт-кода, что в нативной версии, только трансляция выполнилась раньше в процессе сборки.
Смысл один - весь dart код всегда работает под управлением одной и той же внешней системы. Называйте ее как хотите VM / Runtime. Принцип функционирования от этого не меняется.
Пришлось сходить на официальный сайт чтобы поставить точку:
dart compile exe
A standalone, architecture-specific executable file containing the source code compiled to machine code and a small Dart runtime.
Идем по ссылке что такое Dart runtime:
On native platforms, the Dart runtime is automatically included inside self-contained executables, and is part of the Dart VM provided by the dart run command.
Вот и весь рантайм - всего лишь обрезанная часть той самой DartVM. Без функций оптимизации и динамической загрузки.
Очевидно потому что asMap() всегда вернет Map<int, ЧтоТо> а этот метод возвращает Map<String, Object?>, т.е. трансформирует ["key1", value1, "key2", value2...] в {"key1": value1, "key2": value2 ...}
Я повторюсь еще раз - единственная разница между VM языка и Runtime только в том что с VM вы берете свой файл и без изменений его переносите между linux/windows/ЧемТоЕще. Или прибиваете гвоздями в свой готовый файл и собираете уже отдельно для linux/windows/ЧтоТоЕще. .Net runtime, Node.js - это тоже runtime только вот никакой разницы с JavaVM у них нет. И Dart runtime точно такой же, просто он включен в вашу программу и не требует установки отдельно.
Ну в итоге ответ на ".. что вроде бы должно давать максимальную производительность..." - по тестам Dart код проигрывает в производительности нативному C++ в 2-3 раза.
Это согласно тестам в которых использовались оба языка:
Dart vs C++
Конечно есть - накладные расходы.
Собственно этот обмен мнениями и начался с цитаты про нативник dart:
".. он компилируется в бинарный код платформы – что вроде бы должно давать максимальную производительность.."
Вы говорите о том что можете сделать в нативе. В конце концов DartVM сама написана на C++ и при желании можно хоть повторить ее в своем коде.
Но код Dart сам по себе никогда не может быть так запущен. Он оборачивается в task и встает в очередь EventLoop.
Но ведь JavaVM при работе постепенно перекомпелирует байткод в нативный (прогревается), спустя какое-то время вся система может быть уже в нативном виде.
Получается VM плавно мутирует в рантайм? :)
В реальном нативе, тот же C/C++. Ваша программа набор инструкций процессору. Которые он будет выполнять без остановки, пока они не закончатся. И нет в собранном бинарнике никаких Loop. Все инструкции идут шаг за шагом без остановок. Ну только системный планировщик их приостанавливает, рулит их приоритетом.
Ваш код Dart это не иснтрукции процессору а инструкции DartVM. Хоть они и уже в бинарном виде. Но запустит их именно DartVM в виде своих task/microtask. И он может менять их порядок выполнения для всяких await/thien.
Хотите "чистой архитектуры" - возьмите за правило никогда не писать во flutter коде ничего что не рисует картинку. Вообще ничего. Все что хоть что-то считает - отдельная библиотека на чистом dart.
Это правило для себя я вывел на первой приложуньке.
На второй правило дополнилось - все что считает хоть что-то обязано всегда инициироваться с Isolate.spawn(). Ничего считающего, получающего из сети, и вообще делающее хоть что-то не рисующее не может быть в основном изоляте с рендерингом. Основной изолят должен только получать что-то от других и соответсвенно реагировать на эти сообщения.
Я не особый сторонник "чистой архитектуры" но с этими правилам что-то подобное получается само по себе.
Код ни в Dart ни в Java не запускается сам по себе. Его запускает JavaVM или DartVM. В случае Dart это тот самый Loop который строит очередь тасков/микротасков и построчно выполняет каждый шаг программы. Как не обзовите НативнаяКомпиляция/Предкомпиляция - все равно каждое действие проходит через очередь DartVM. Она рулит потоком управления.
Все деления Runtime/VM - чистый маркетинговый флуд. Ну или нужно ставить VM отдельно / она включена в ваш код.
Я собираю один единый exe с помощью GraalVM для десктоп на JavaFX.
Приложение полностью независимо, один exe не требует никаких предустановленных компонентов на машине.
Но это все та-же JavaVM только находится внутре того-же exe.
Это теперь Runtime или VM?
И что тут опровергает "образ DartVM + ваш код"?
Именно DartVM работает с память, обслуживает ваши изоляты (ведь main запускается в изоляте). Абсолютно то же самое делает и java. Только Java по ходу исполнения переводит байт код в нативные (прогрев) и в теории может что-то оптимизировать под конкретное железо. А Dart это сделает сразу на "усредненую" машину.
Минимальный exe "Hello world" который собирает Dart под Windows - 5Mb. Если бы он собирал несколько килобайт как это делают C/C++ можно было бы рассуждать о неком настоящем нативе.
Дарт просто собирает нативный образ DartVM + ваш код в виде натива, который может запустить система. А по сути это та же самая а-ля javaVM, только маленькая.
Зачем верить или нет. Нужно знать. Идем в исходники flutter и смотрим определение setState (отбросим assert, так как их все равно нет в рабочем приложении):
void setState(VoidCallback fn) {final Object? result = fn() as dynamic;
_element!.markNeedsBuild();
}
Ну можем еще markNeedsBuild() проверить:void markNeedsBuild() {
if (dirty) {return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
Ничего setState не перестраивает, только меняет булев флаг dirty.
Так что сколько его не жмакай хоть каждые 1 наносекунду перестройка будет не чаше чем раз на кадр.
И в статье речь не про сам setState а что лучше использовать виджеты анимации.
Но внезапно все анимационные виджеты наследуются от AnimatedWidget, который сам по себе StatefulWidget. И все анимации жмакают этот самый setState сразу после текущего кадра. Т.е. кадр отработался, они тут же считают значения на следующий кадр и банально дергают setState.
А вот неправда, если вы только сами себе проблем не сделали.
Листы по умолчанию заворачивают своих детей в RepaintBoundary. А это блокирует перерисовку вложенных виджетов. Поэтому при скролле дети не пересоздаются, хотя казалось бы дерево меняется.
Только если вы следуете "лучшим практикам иммутабельности" - самой идиотской идиоме, и на каждое движение пересоздаете весь список нижележащих данных.
Тогда да, убогая иммутабельность неизлечима :)
Я не очень понимаю что за проблема с "преобразованием и возвращаемым значением". Для каждого вызова можно сделать свои static функции которые будет получать или возвращать то что нужно.
Но главное в этом всем одно - ваш подход это только если вы сами автор и натива и java кода. И можете сразу все править для точного соответствия классов и натива.
Если ваш натив предполагает быть библиотекой для широкого использования или даже просто использоваться в нескольких собственных проктах то это породит кучу прослоек под каждый чих.