Comments 37
Делал подобное на vb6 лет 15 назад. Правда там использовал известную для ВэБэшников api функцию CallWindowProc. Впрочем, метод (как тот, так и описанный здесь в статье) прибит гвоздями к x86.
Для работы с указателями и ref
— структурами можно посмотреть в сторону
System.Runtime.CompilerServices.Unsafe. Класс доступен как в netcore3.0
так и в netstandard2.0
как самостоятельный nuget
-пакет.
Позволяет кастовать (в определенных случаях) ссылки к указателям и обратно, а так же "безопасно" выполнять арифметику над указателями.
Как насчет написать процедуру в байт-кодах и передать управление в нее?
Генерация IL-кода на лету доступна "из коробки".
Google "ilgenerator", например: https://habr.com/ru/company/skbkontur/blog/262711/
А вот возиться с нативным ассемблером из C# не вижу смысла.
А почему не использовали Mono?
Там что то упоминалось про вставки на ассемблере если вы компилируете в наитивный код (не знаю -не слежу, убрали ли эту возможность в связи с объединением кодовой базы с Net ).То есть получается исполняемый файл под конкретную архитектуру, если на размер плевать то можно сделать автономным, не завищищий есть ли у тебя mono -среда,.А если что вызывать из ехешника библиотеки среды исполнения, тогда размер поменьше.
Если уж извращаться, то почему бы не через expressions?
Типа
Action a = Asm.Compile((ptr)=>{
var label1 = Asm.Mov(Asm.Ecx, ptr);
Asm.Cmp(Asm.Ecx, Asm.Edx);
Asm.Je(label1);
})
Негативных факторов слишком много.
Насколько я помню, первые попытки отмазаться встроенным ассемблером начались в Дельфях. Хотя сильно не уверен в первенстве.
Но главный вопрос — а нафига?
Вообще возможно собрать что-то на синтаксисе C# что будет компилироваться в идеально прилизанный нативный код.
У разработчиков Unity в блоге есть занимательный текст (на английском) как они перевели performance critical вещи с C++ на C# и вполне счастливы, суть претензий — в борьбе с компиляторами С++ где векторизация имеет склонность тихо отваливаться.
Но, конечно, уже это не совсем C#
А потом я переписал эту программу на C#/Mono, и она с ходу заработала ещё быстрее.
А вчера я начал баловаться с Rust, и переписанная на нём программа заработала ещё в полтора раза быстрее.
У C# (да и прочих IL-языков) никаких шансов пробиться в топ по производительности.
Но это связано с компиляцией в процессе работы программы, а не с самим фактом существования IL-кода. Тот же LLVM, но со статической компиляцией, вполне себе быстр.
Но главный вопрос — а нафига?
Вот именно. Поначалу я пытался использовать ассемблер для SSE/AVX оптимизаций. Потом понял бесперспективность данного направления из-за необходимости писать ветки кода и для x86, и для x64 архитектуры и открыл для себя интринсики.
Приведенная реализация InvokeAsm может в любой момент поломаться из-за GC. Ну хорошо, VirtualProtect работает надёжно потому что в блоке fixed — а дальше-то что помешает массиву байт быть перемещенным в другое место?
InvokeAsm
обернуть в метод, запрещающий сборку мусора так же, как это сделано в SafeInvokeAsm
.Честно говоря, такое решение мне кажется не совсем элегантным, так что если кто-либо сможет предложить иной вариант борьбы с GC — с удовольствием почитаю.
Так ведь правильное решение уже подсказали выше: явно выделить кусок памяти вне кучи. Лучше даже через VirtualAlloc.
А потом можно получить управляемый делегат через Marshal.GetDelegateForFunctionPointer
. Только надо придумать как связать время жизни делегата и время жизни участка памяти, похоже придётся ещё и управляемый делегат налету компилировать, с вызовами DangerousAddRef и DangerousRelease...
Пока хэндл не будет освобождён, этот кусок памяти будет прибит гвоздями и gc не будет его трогать.
Лет 7-8 назад качество компиляции VS оставляло желать лучшего. Код на интринсиках работал в 2-3 раза медленее вручную написанного на ассемблере даже с максимальными оптимизациями. А потом я открыл для себя интеловский компилятор.
2. Выкидываем все вышеперечисленное и пишем на асме
3. ???
4. PROFIT!
Пример там не абстрактный, а вполне рабочий, применяемый в реальных проектах.
Ну а что касается ассемблера, использовать его в C# смысла не вижу, язык создан не для этого. Кроме того, теряется кроссплатформенность, которая является главной (на мой взгляд) фишкой .NET Core, а за ним будущее всего .NET.
Что-то там у вас тоже всё слишком сложно вышло, можно же использовать System.Linq.Expressions для той же цели.
Ассемблерные вставки… в C#?