Приведены аналоги но не прямые. Можно упомянуть про более прямые аналоги вроде RedPanda, у которого по не самым свежим бенчмаркам в 3 раза выше скорость записи и в 20 раз выше скорость чтения.
Попробовал #[inline(never)], символ в so действительно появился, но в приложении bin он использоваться не стал внутри трампляна lang_start_internal как в случае no_mangle. Хотя зависимость от so как от мертвого кода не пропала.
Насчет extern "C" и no_mangle все же не согласен, посмотрел еще примеры и докумендацию. Везде одно и то же https://doc.rust-lang.org/std/keyword.extern.html extern - пишем у прототипа который импортаруем, причем по дефолту он "C", для c->rust. Для rust->с без #[unsafe(no_mangle)] никак, это официальный метод, причем extern "C" можно написать, а можно опустить, он по дефолту. То есть приделываем си интерфейс растовской либе имено что через no_mangle и никак иначе.
error: attribute must be applied to a `static` variable
--> src/dylib/src/lib.rs:1:1
|
1 | #[used]
| ^^^^^^^
2 | / pub fn add(left: usize, right: usize) -> usize {
3 | | left + right
4 | | }
| |_- but this is a function
Другие компиляторы тоже не стоят на месте, на том же clang это тоже должно было сказаться. Конечно все хотят и пооптимизирование и побыстрее. Поинт же не в этом, поинт в том что архитектура такая - что даже инкрементальная сборка делает львиную долю компиляции. Про то что analyze шаг тоже не бесплатный вообще молчю, он доложен пройтись по всему "миллиону" файлов, их отркрыть и прочитать чтобы проанализировать, это вот вообеще не про собрал либу и пользуйся.
Похоже это не просто какой-то inline, похоже он вообще полностью проигнорировал dylib, достал где искать исходник и вкомпилил все из исходников, то-есть в релизе dylib не dylib.
Так же похоже терминирует он не в произвольный свой ABI, а в C++, что make sense при использовании llvm, там даже манглинг имен от с++, а so зависит от libgcc.
То есть как я понял никакой поддержки своего нестабильного ABI нет, ну кроме добавления в конце хеша в с++ манглинг, потому то и используется только в дебаге. А в релизе использование из разных мест может требовтаь разного ABI, потому терминирование вообеще всего в с++ ABI, включая кусков рантайма пытаются избежать.
Есть еще проблема: в dylib включаются только куски рантайма которые нужны. И если cargo не позаботится влинковать в прогу все что надо для dylib, - он очевидно не загрузится. Нам просто повезло что наш add зависит только от global_dtors и panic_const_add_overflow, потому даже если рантайм исключим то загружается. А вот более сложный пример уже не работает, ну и если в приложенине вообще подсовывать dylib`ы о которых оно не знает то тут даже cargo никак теоретически не спасет. Может есть возможность влинковать в приложение полный раст рантайм, но я пока не знаю как.
Так что вот, в контексте времени сборки большого проекта и использования большой либы, - пока все же есть проблемы, даже в рамках одной точной версии компилятора (чего тоже не достаточно).
0000000000004008 b completed.0
0000000000001040 t deregister_tm_clones
00000000000010b0 t __do_global_dtors_aux
0000000000003e30 d __do_global_dtors_aux_fini_array_entry
0000000000004000 d __dso_handle
0000000000003e38 d _DYNAMIC
00000000000010fc t _fini
00000000000010f0 t frame_dummy
0000000000003e28 d __frame_dummy_init_array_entry
0000000000002000 r __FRAME_END__
0000000000003fc8 d _GLOBAL_OFFSET_TABLE_
0000000000001000 t _init
0000000000001070 t register_tm_clones
0000000000000000 N rust_metadata_dylib_8f917b1ee30d8ed1
0000000000004008 d __TMC_END__
Да вы сами можете поэксперементировать на репе выше.
Как уже писал выше, речь чтобы обновление компилятора тоже переживало, иначе проблем не оберешся, на билдере одна версия, локально другая и все, приехали.
Но проблема не только в этом, теоретически сделать можно было бы, но в rust оно пока не до конца нормально работает по моим эксперементам, вот по всем типам: cdylib staticlib - для C ffi, вычеркиваем. rlib - архив где объектник с мета инофрмацией, там есть пути до файлов на это машине, перемещать его нельзя. dylib - для простого сложения генерит 14/2mb debug/release .so, это не то что бы хотелось для простого add(), особенно если потом это а приложение подкладывать и их несколько. Способа не дублировать рантайм в них я пока не нашел, когда для традиционных либ несколько экземпляров code1.so code2.so -> one_runtime.so/exe обычное дело.
Часть из этих зависимостей - биндинги к сишным либам, например opus. Ну и из-за того что зависимостей много и имеем аж 2 минуты, это не так мало, но аналогичный сишный код должен тем же clang компилиться примерно столько же. Не вижу причин почему нет т.к. львиная доля оптимизирующего компилятора ir->машинный код по сути через тот же бэк идет, у rust только различие что почти все блоки noalias помечены.
ewext к слову тоже на rust, и использует C ABI для ffi, конечно.
Ну вот о чем и речь, единственный вариант как сейчас это можно сделать. А так бы вы ее тоже собирали, и так очевидно до любого времени сборки\пересборки можно дойти, вопрос лиш в объеме проекта.
Ну так кода мало совсем, аналогичное кол-во кода на си тоже будет быстро собираться. К тому же вы там используете lua и ewext.dll, как бы вы к ним вклинились без ABI? ABI нужно чтобы была точка входа, где один раз собрал, - много раз использовал во многих местах. Расставлять эти точка разумеется желательно так что скорость выполнения страдала минимально. Хэшмапы будут уже внутри большой либы, внутри либы и ABI им действительно не нужен и если символы корректно скрыты, то с этим все должно быть в порядке и в C/C++.
Конечно мешает, у инкрементальной сборки есть специальный шаг analysis, он и делает все возможное чтобы исключить то что не надо, но если вы посмотрите на шаг "optimization and codegen" инкрементальной сборки, то оно все еще ест львиную долю на реальных проектах. Пример бенчмаркоа можно посмотреть например тут: https://blog.rust-lang.org/2016/09/08/incremental/ Статья не самая свежая, если у вас есть бенчмарки новее - кидайте, мне тоже интересно было бы посмотреть.
Ну и "пользоваться без проблем" включает в себя в том числе переживание апдейтов компилятора, хотя-бы минорных. И вообще частая ситуация, когда толстая либа вообще на билдере собирается, а ты потом и локально и на другом билдере можеш собирать. Держать везде окрушение прям совсем одинаковое весьма проблематично, да и cargo пока не поддерживает режим где тебе с билдера промежуточные объектники выдаст, которые в инкрементальной сборке можно дальше использовать.
И ни слова об отсутствии стабильного ABI и его влиянии на время сборки больших проектов. Вот допустим у нас есть мега-фреймворк на C++ и Rust типа Qt, допустим собирали мы его несколько часов. Можем ли мы теперь им просто пользоваться и почти мгновенно пересобирать свое придожение? С++ - да, Rust - нет, даже с хаками и плохой скоростью.
Единственное исключение - это LTO, если хочется всю программу с ним собрать то сборка релиза будет одного порядка скорости, по крайней мере с llvm бэком. Но LTO сборка обычно и не нужна каждый раз, это выжимание финальных ~5% на финальном этапе.
А вот попытка ускорения компиляции в разы чтобы было хотя-бы не так долго собиралось - явно ударит по производительности так что может стать не комфортно пользоваться для теста, и не понимаеш это так из-за сборки или пора оптимизировать, бенчмаркать его уже невозможно. Хаки с ir и патчинг врядли будут когда-то до конца юзабельными из-за архитектуры когда малое изменение одного крейта затрагивает кодогенерацию по всему приложению. jit на лету тоже такой себе аналог java для нативного приложения.
Пока единственное решение взять достаточно большую либу Rust и пользоваться без проблем - приделать к ней C интерфейс.
Эм, нет, оно выбранное просто печатает в stdout. То есть даже с хоткеем чтобы выбранное было готово к редактирования в командной строке, надо 2 доп действия - 1. скопировать из stdout, причем строка может не влезть в 1 строку на экране 2. вставить в командную строку. Тут же даже строка не вмещающаяся на экран будет сразу готова.
Про это я знаю. Это и есть "история куда менее удобная", оно показывает одну единственную строку, а не весь список сразу, который можно листать в том числе pg-up/down. Во такое что-то подобное есть?
В фаре у консоли если что тоже есть доп фишки. Например набираеш ff - и вот список всех последних вызовов ffmpeg, можно быстро выбрать подходящий и отредактировать. В стандартной консоли большинства дистрибутиовов история куда менее удобная. А вы какой пользуетесь? Из нестандартных что-то подобное есть?
Не система типов, а то что может быть только одна мутабельная ссылка, что дает возможность считать почти все указатели не пересекающиммися. На си в оптимизированном коде это тоже оптимизируют просто вручную, или можно даже весь код скомпилить считая что этого нет, и если есть пересечения код просто сломается.
Причина скорее всего более банальна - в дефолтных ключах компиляции, rust использует lto по дефолту, в zlib скорее всего с системным сравнивали где ключи совсем другие. Так же для си надо включать всякие -U_FORTIFY_SOURCE иначе по дуфолту будут проверки вызовов из glibc. Если все это исключить то код на том же clang дожен быть взаимовыражаемым.
Так же в той статье уже упомянули libdeflate, коотрый быстрее zlib-ng в большинстве случаев, так что почему с ним только сравнение непонятно: https://github.com/zlib-ng/zlib-ng/issues/1486
Приведены аналоги но не прямые. Можно упомянуть про более прямые аналоги вроде RedPanda, у которого по не самым свежим бенчмаркам в 3 раза выше скорость записи и в 20 раз выше скорость чтения.
Можно еще упомянуть про dosbox,старые игры обычно эмулируют именно им.
Попробовал #[inline(never)], символ в so действительно появился, но в приложении bin он использоваться не стал внутри трампляна lang_start_internal как в случае no_mangle. Хотя зависимость от so как от мертвого кода не пропала.
Насчет extern "C" и no_mangle все же не согласен, посмотрел еще примеры и докумендацию. Везде одно и то же
https://doc.rust-lang.org/std/keyword.extern.html
extern - пишем у прототипа который импортаруем, причем по дефолту он "C", для c->rust.
Для rust->с без #[unsafe(no_mangle)] никак, это официальный метод, причем extern "C" можно написать, а можно опустить, он по дефолту.
То есть приделываем си интерфейс растовской либе имено что через no_mangle и никак иначе.
Так сработало, но судя по документации
https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
Оно unsafe. Так же учитывая сишный манглинг и по факту получившийся ABI это можно считать C FFI.
Точнее не можно, а похоже это прям он и есть, в c-to-rust ffi примерах, как раз и используется no_mangle:
https://github.com/alexcrichton/rust-ffi-examples/blob/master/c-to-rust/src/lib.rs
Ну то есть о чем изначально и говорилось.
Не прокатило:
Ну а static извне уже не подхватится.
Другие компиляторы тоже не стоят на месте, на том же clang это тоже должно было сказаться. Конечно все хотят и пооптимизирование и побыстрее. Поинт же не в этом, поинт в том что архитектура такая - что даже инкрементальная сборка делает львиную долю компиляции.
Про то что analyze шаг тоже не бесплатный вообще молчю, он доложен пройтись по всему "миллиону" файлов, их отркрыть и прочитать чтобы проанализировать, это вот вообеще не про собрал либу и пользуйся.
Похоже это не просто какой-то inline, похоже он вообще полностью проигнорировал dylib, достал где искать исходник и вкомпилил все из исходников, то-есть в релизе dylib не dylib.
Так же похоже терминирует он не в произвольный свой ABI, а в C++, что make sense при использовании llvm, там даже манглинг имен от с++, а so зависит от libgcc.
То есть как я понял никакой поддержки своего нестабильного ABI нет, ну кроме добавления в конце хеша в с++ манглинг, потому то и используется только в дебаге. А в релизе использование из разных мест может требовтаь разного ABI, потому терминирование вообеще всего в с++ ABI, включая кусков рантайма пытаются избежать.
Есть еще проблема: в dylib включаются только куски рантайма которые нужны. И если cargo не позаботится влинковать в прогу все что надо для dylib, - он очевидно не загрузится. Нам просто повезло что наш add зависит только от global_dtors и panic_const_add_overflow, потому даже если рантайм исключим то загружается. А вот более сложный пример уже не работает, ну и если в приложенине вообще подсовывать dylib`ы о которых оно не знает то тут даже cargo никак теоретически не спасет. Может есть возможность влинковать в приложение полный раст рантайм, но я пока не знаю как.
Так что вот, в контексте времени сборки большого проекта и использования большой либы, - пока все же есть проблемы, даже в рамках одной точной версии компилятора (чего тоже не достаточно).
Да, в дебаге есть символ, а в релизе нету, вот такие странности.
Сгенерился пустой .so:
Да вы сами можете поэксперементировать на репе выше.
Как уже писал выше, речь чтобы обновление компилятора тоже переживало, иначе проблем не оберешся, на билдере одна версия, локально другая и все, приехали.
Но проблема не только в этом, теоретически сделать можно было бы, но в rust оно пока не до конца нормально работает по моим эксперементам, вот по всем типам:
cdylib staticlib - для C ffi, вычеркиваем.
rlib - архив где объектник с мета инофрмацией, там есть пути до файлов на это машине, перемещать его нельзя.
dylib - для простого сложения генерит 14/2mb debug/release .so, это не то что бы хотелось для простого add(), особенно если потом это а приложение подкладывать и их несколько.
Способа не дублировать рантайм в них я пока не нашел, когда для традиционных либ несколько экземпляров code1.so code2.so -> one_runtime.so/exe обычное дело.
Вот минимальный пример https://github.com/rob9315/corrosion-dylib
Часть из этих зависимостей - биндинги к сишным либам, например opus.
Ну и из-за того что зависимостей много и имеем аж 2 минуты, это не так мало, но аналогичный сишный код должен тем же clang компилиться примерно столько же.
Не вижу причин почему нет т.к. львиная доля оптимизирующего компилятора ir->машинный код по сути через тот же бэк идет, у rust только различие что почти все блоки noalias помечены.
Ну вот о чем и речь, единственный вариант как сейчас это можно сделать. А так бы вы ее тоже собирали, и так очевидно до любого времени сборки\пересборки можно дойти, вопрос лиш в объеме проекта.
Ну так кода мало совсем, аналогичное кол-во кода на си тоже будет быстро собираться.
К тому же вы там используете lua и ewext.dll, как бы вы к ним вклинились без ABI?
ABI нужно чтобы была точка входа, где один раз собрал, - много раз использовал во многих местах. Расставлять эти точка разумеется желательно так что скорость выполнения страдала минимально.
Хэшмапы будут уже внутри большой либы, внутри либы и ABI им действительно не нужен и если символы корректно скрыты, то с этим все должно быть в порядке и в C/C++.
Конечно мешает, у инкрементальной сборки есть специальный шаг analysis, он и делает все возможное чтобы исключить то что не надо, но если вы посмотрите на шаг "optimization and codegen" инкрементальной сборки, то оно все еще ест львиную долю на реальных проектах.
Пример бенчмаркоа можно посмотреть например тут: https://blog.rust-lang.org/2016/09/08/incremental/
Статья не самая свежая, если у вас есть бенчмарки новее - кидайте, мне тоже интересно было бы посмотреть.
Ну и "пользоваться без проблем" включает в себя в том числе переживание апдейтов компилятора, хотя-бы минорных. И вообще частая ситуация, когда толстая либа вообще на билдере собирается, а ты потом и локально и на другом билдере можеш собирать. Держать везде окрушение прям совсем одинаковое весьма проблематично, да и cargo пока не поддерживает режим где тебе с билдера промежуточные объектники выдаст, которые в инкрементальной сборке можно дальше использовать.
И ни слова об отсутствии стабильного ABI и его влиянии на время сборки больших проектов.
Вот допустим у нас есть мега-фреймворк на C++ и Rust типа Qt, допустим собирали мы его несколько часов.
Можем ли мы теперь им просто пользоваться и почти мгновенно пересобирать свое придожение?
С++ - да, Rust - нет, даже с хаками и плохой скоростью.
Единственное исключение - это LTO, если хочется всю программу с ним собрать то сборка релиза будет одного порядка скорости, по крайней мере с llvm бэком. Но LTO сборка обычно и не нужна каждый раз, это выжимание финальных ~5% на финальном этапе.
А вот попытка ускорения компиляции в разы чтобы было хотя-бы не так долго собиралось - явно ударит по производительности так что может стать не комфортно пользоваться для теста, и не понимаеш это так из-за сборки или пора оптимизировать, бенчмаркать его уже невозможно. Хаки с ir и патчинг врядли будут когда-то до конца юзабельными из-за архитектуры когда малое изменение одного крейта затрагивает кодогенерацию по всему приложению. jit на лету тоже такой себе аналог java для нативного приложения.
Пока единственное решение взять достаточно большую либу Rust и пользоваться без проблем - приделать к ней C интерфейс.
Спасибо за скрипт, действительно работает. 👍
Эм, нет, оно выбранное просто печатает в stdout. То есть даже с хоткеем чтобы выбранное было готово к редактирования в командной строке, надо 2 доп действия - 1. скопировать из stdout, причем строка может не влезть в 1 строку на экране 2. вставить в командную строку.
Тут же даже строка не вмещающаяся на экран будет сразу готова.
Про это я знаю. Это и есть "история куда менее удобная", оно показывает одну единственную строку, а не весь список сразу, который можно листать в том числе pg-up/down. Во такое что-то подобное есть?
В фаре у консоли если что тоже есть доп фишки. Например набираеш ff - и вот список всех последних вызовов ffmpeg, можно быстро выбрать подходящий и отредактировать.
В стандартной консоли большинства дистрибутиовов история куда менее удобная.
А вы какой пользуетесь? Из нестандартных что-то подобное есть?
Не система типов, а то что может быть только одна мутабельная ссылка, что дает возможность считать почти все указатели не пересекающиммися. На си в оптимизированном коде это тоже оптимизируют просто вручную, или можно даже весь код скомпилить считая что этого нет, и если есть пересечения код просто сломается.
Причина скорее всего более банальна - в дефолтных ключах компиляции, rust использует lto по дефолту, в zlib скорее всего с системным сравнивали где ключи совсем другие. Так же для си надо включать всякие -U_FORTIFY_SOURCE иначе по дуфолту будут проверки вызовов из glibc. Если все это исключить то код на том же clang дожен быть взаимовыражаемым.
Так же в той статье уже упомянули libdeflate, коотрый быстрее zlib-ng в большинстве случаев, так что почему с ним только сравнение непонятно: https://github.com/zlib-ng/zlib-ng/issues/1486
Совместимость есть в примерах из RFC (вторая ссылка), как это объединять с первой будут мне тоже не понятно, о чем тоже написал.