Entry tags:
динамическая загрузка Dex файла в Dalvik
В общем и с явовской виртуальной машиной вышло по сценарию -- "хотели как лучше, а получилось -- как всегда." ;-) Вроде бы делали универсальный байткод, который write once, run anywhere... Сделали. Но теперь неймется компании Google, которая мало того, что написала для Явы новую виртуальную машину, да еще к тому-же и совершенно другой архитектуры.
Большинству, конечно, все равно, они свои программы перекомпилируют специальным гуглевским компилятором (который из обычных Java class files делает Dalvik-овские dex файлы). А что делать нам, несчастным писателям компиляторов ? ;-)
Но мало того, то байткод другой архитектуры. Еще и загружать динамически его приходится по-новому. И не просто по-новому, оказывается сам этот байткод Dalvik загрузить непосредственно не может, только из zip архива... В общем, грабли на граблях.
Ну вот я вроде разобрался. Пишу, чтобы зафиксировать. Вам это, скорее всего, будет не интересно. ;-)
Итак, возьмем программу Hello.java с одним единственным методом hello, который очевидно что делает. Задача в том, чтобы загрузить этот файл в Андроид динамически, непосредственно в двоичном виде.
Тоесть загрузить нужно Вот этот zip файл, созданный командой:
Ну не жуть ли ? ;-)
Так что, наступает эра многоплатформенных компиляторов в явовские байткоды со всеми вытекающими "прелестями". Да, явовский байткод не очень оптимален в плане используемого пространства, но неужели такая оптимизация, всего-лишь в несколько раз, стоила того ? И все равно-ж, гады, в процессе загрузки этого .zip с файлом .dex (вызов конструктора DexFile), они докомпилируют его в "оптимизированный" байткод в формате .odex, который еще хранят в отдельном файле.
Вот такая вот бессмысленная политика (ибо по-другому мне это сложно объяснить). Нет, для меня это, конечно, выгодно. Я то думал проект уже закончен. А тут опа, появляется возможность переписать все заново (с сохранением старого, проверенного и удобного API). Типа раз, и снова в девках. ;-) Хорошо, но бессмысленно.
По-хорошему нужно было спрятать свой dalvik подальше (как implementation detail, раз уж они верят, что именно такой подход даст им performance edge), включить в стандартную поставку перекомпилятор dx в прозрачном режиме и пройти явовский testsuite.
Большинству, конечно, все равно, они свои программы перекомпилируют специальным гуглевским компилятором (который из обычных Java class files делает Dalvik-овские dex файлы). А что делать нам, несчастным писателям компиляторов ? ;-)
Но мало того, то байткод другой архитектуры. Еще и загружать динамически его приходится по-новому. И не просто по-новому, оказывается сам этот байткод Dalvik загрузить непосредственно не может, только из zip архива... В общем, грабли на граблях.
Ну вот я вроде разобрался. Пишу, чтобы зафиксировать. Вам это, скорее всего, будет не интересно. ;-)
Итак, возьмем программу Hello.java с одним единственным методом hello, который очевидно что делает. Задача в том, чтобы загрузить этот файл в Андроид динамически, непосредственно в двоичном виде.
Тоесть загрузить нужно Вот этот zip файл, созданный командой:
javac Hello.java && \ dx --dex --output=Hello.zip Hello.class && \ adb -e push Hello.zip /data/localЧтобы это сделать, нужна вот такая программа SayHello.java, которая компилируется и работает вот так:
javac SayHello.java && \ dx --dex --output=SayHello.zip SayHello.class && \ adb -e push SayHello.zip /data/local \ adb -e shell dalvikvm -classpath /data/local/SayHello.zip SayHello Note: SayHello.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 43 KB/s (2034 bytes in 0.045s) Hi ! Im' your "Hello World" loader ! First, I'm hooking the DexFile class...Good. Second, I'm getting hold of its constructor...Good. Third, I'm instantiating the DexFile object on "/data/local/Hello.zip"...Good. Fourth, I'm getting to its loadClass method...Good. Fifth, loading Hello class from the loaded dex...Good. Sixth, getting hello method of Hello class...Good. Seventh, invoking the hello method: Hello WORLD ! Done. Easy, isn't it ? ;-)
Ну не жуть ли ? ;-)
Так что, наступает эра многоплатформенных компиляторов в явовские байткоды со всеми вытекающими "прелестями". Да, явовский байткод не очень оптимален в плане используемого пространства, но неужели такая оптимизация, всего-лишь в несколько раз, стоила того ? И все равно-ж, гады, в процессе загрузки этого .zip с файлом .dex (вызов конструктора DexFile), они докомпилируют его в "оптимизированный" байткод в формате .odex, который еще хранят в отдельном файле.
Вот такая вот бессмысленная политика (ибо по-другому мне это сложно объяснить). Нет, для меня это, конечно, выгодно. Я то думал проект уже закончен. А тут опа, появляется возможность переписать все заново (с сохранением старого, проверенного и удобного API). Типа раз, и снова в девках. ;-) Хорошо, но бессмысленно.
По-хорошему нужно было спрятать свой dalvik подальше (как implementation detail, раз уж они верят, что именно такой подход даст им performance edge), включить в стандартную поставку перекомпилятор dx в прозрачном режиме и пройти явовский testsuite.
no subject
Как я себе представляю, многих проблем, для которых придуманы красивые методы оптимизации, в стековой архитектуре просто бы не было.
Ведь регистры -- это просто близкая к ALU, специализированная кэш-память. В стековой архитектуре они не нужны вообще, вместо них была-бы просто кэш-память, не специализированная, которая хранила бы наиболее часто адресуемые адреса из основной памяти, содержащей номинально безграничный стек. Это позволило-бы делать процессоры более масштабируемыми, сделало бы оптимизацию более динамической (что хранить в кэше на этапе выполнения программы виднее, чем на этапе статического ее анализа). Не нужно было-бы перекомпилировать программу под разные под-типы процессоров, с разной шириной и количеством регистров, а просто процессор с большим кэшем, автоматически использовал бы его полностью и содержал бы по-сути большее количество регистров совершенно прозрачно для программиста и компилятора.
Стековая архитектура (с кэшем вместо регистров и неограниченным стеком) лучше в многозадачной среде, поскольку при переключении задач не нужно было бы сохранять содержимое всех регистров, задачи совершенно прозрачно использовали бы по-максимуму общий кэш.
Никаких проблем нет и с суперскалярной архитектурой. В стековой машине можно применить по-сути целиком и полностью register renaming, только переименовывать не регистры, а несколько верхних элементов стека (сколько -- опять-же было-бы всего-лишь несущественной деталью реализации), что позволило бы развязать кажущиеся зависимости по данным и выполнять несколько инструкций параллельно.
В стеке, конечно, нужно хранить не какие-то конкретные типы данных, а машинные слова. Типы данных определяются поверх машинных слов, это более высокоуровневая концепция.
Многие вещи, которые в регистровой архитектуре можно оптимизировать, как, например, распределение регистров (особенно если они немного различаются функционально, как в x86, что делает проблему нетривиальной), в стековой оптимизировать не нужно, поскольку нет регистров -- нет проблем.
Поскольку инструкции простые и "свободы" в них мало, львиная доля оптимизации была бы в процессоре (а не в компиляторе), который имеет возможность подстроиться под конкретный свой обьем кэша и времена выполнения каждой инстркции. Это, повторюсь, позволило бы сделать процессоры более масштабируемыми. Мыслимо было бы сделать целую линейку процессоров (от встраиваемых и до высокопроизводительность), которые выполняли бы одни и те-же команды без перекомпиляции (и без черной магии в виде instruction scheduling) с максимальной эффективностью на каждом из них. Причем, транзисторов для этого потребовалось бы меньше (а значит их можно было бы использовать для дополнительного параллелизма, лучшего управления кэшем-регистрами и т.д.).
К.Л.М.
no subject
управления кэшем-регистрами --> управления кэш памятью, играющей роль регистров