
Комментарии 73

Мне кажется, что грузить dll и дергать puts - это уже читерство. Вполне может оказаться что в одной из стандартных библиотек уже есть строка "Hello, world", например.
Плюсую нещадно.
Оптимизация лишней не бывает, и это относится к коду, бинарнику, базам, запросам.
В какой-то момент за неряшливый код приходится платить.
Да и эстетическое удовольствие - читать красивый код.
Такое скорее нужно именно вирусописателям, но никак не при нормальной инженерной практике. Хотя с познавательной точки зрения статья огонь!
Вот именно что с познавательной точки зрения.
Вирусописателям? Не дотнете? Серьезно?
Их полно. Вирус это не только что-то работающее в режиме ядра, - вирус может быть многокомпонентным, например взаимодействующим с сетью - а это поверьте удобнее на дотнете делать, скрывая процесс тем же драйвером ядра, или даже внедрять свой дотнет код в существующий процесс
Sweet summer child...
Даже на хабре помню, что была как минимум одна статья, где разбирался вирус на .NET
Мы когда то ботоводили в игре одной, а чтобы античит не спалил - все обертки были написаны на батниках, а взаимодействие - на autoit, никаких эксплоитов и "подозрительных" процессов. И много лет все работало.
Оптимизация лишней не бывает
Оптимизация всегда лишняя, пока не появилась потребность в этой самой оптимизации. Лучше простой, стабильный, понятный код, чем ускорение запуска на несколько миллисекунд.
Начинаем холивар )
Начинаем холивар )
Зачем?
Мне удовольствие, вам излишество. Уважаю ваше мнение, но остаюсь при своем.
За свободу мнений и вариативность!
Кто-то выше заминусил мой коммент. Мне не понятно, но если челу в кайф, ок. :)
Оптимизация всегда лишняя, пока не появилась потребность в этой самой оптимизации.
Лишняя оптимизация - такая, которая ничем не обоснована.
Есть такие случаи, где заранее известно, что что-то нужно, хотя момент, когда это необходимо ещё не наступил.
"Обоснована" - значит есть осмысленный ответ на вопрос "чтобы что?"
Статья и обсуждение "это нужно вирусописателям" напомнили давние рассказы программистов игровых консолей с жестко ограниченным количеством памяти, отсутствием виртуальной памяти и тп.
Вирусы сейчас тоже жирные ) Потому что грузят свое тело по сети. А такие хаки с уменьшением размера весьма вероятно триггернут антивирус.
Ну на дотнете обычно не пишут там где есть жесткие ограничения по памяти, учитывая вес самого рантайма и минимальный набор рабочего процесса в мегабайт эдак 20-30 :)
Странно, что в 2023-м году и net472.
Ну и да, троллейбус_из_хлеба.жпг
А ещё про разработчиков на C++ говорят что они "байтослесари"...
Шикарная статья. Всем, кто пишет про "зачем", предлагаю ознакомиться с жанром 64k intro и теми высотами, которых он достиг на современных графических API. Я к тому, что эта статья примерно из той же категории. Спасибо за перевод!
Только в данном случае, это равнозначно скрипту на питоне. Он тоже весит килобайты, а если получить байт-код - ещё меньше. Только от этого, он программой не становится
А что насчёт этого?) http://demojs.org/ Не имеет права на жизнь?)
Они и не претендуют называться исполнительным файлом ехе. По сути, дотнет приложения, которые не слинкованы статически вообще могут использовать не ехе расширение, потому что смысла в нем нет, пока нет среды которая бы позволила работать "программе".
Согласен, расширение exe и загрузчик в начале файла тут только ради удобства запуска. Дотнет-сборкам вообще не особо нужно прикидываться PE-файлами.
Я про другое. .NET Framework, по крайней мере определённых версий, считается компонентом Windows. С некоторой натяжкой это в общем-то системное API. Равно как и Direct3D, например. Вопрос - где проходит граница между "программой" и "скриптом на питоне"?) Что должно/не должно быть в бинаре?
Раньше и так граница была размыта, а сейчас ещё больше. Но всё же есть некоторые зацепки. К примеру возьмем любой компилируемый язык с кроссплатформенными возможностями. В зависимости от реализации, мы можем получить либо отсутствие работы углубленно с API (самим фреймворком), т.е. без нативного интерфейса (или имитация), либо реализация нативного интерфейса для всех целевых платформ. Однако, в любом из этих случаев мы имеем зависимость только от самой ОС (на каждой из платформ), а не от чего-то, что было создано для этого языка.
Т.е. ДотНет - это часть именно Windows, а не Линукс или МакОС. Однако, "программа" на C# на Линукс всё ещё зависит именно от ДотНета (части Windows).
.NET Framework, по крайней мере определённых версий, считается компонентом Windows
Нюанс в том что в разных версиях Windows разная версия .NET Framework. А запускать софт, требующий другую версию, оно не может, надо ставить больше фреймворков.
Не забудьте посчитать размер самого дотнета. Или, слинкуйте статический "программу".
Устанавливаемые зависимости из вашего примера не являются обязательными для каждой программы. Ты можешь их не использовать или использовать их в качестве исходников и собрать бинарь. А в случае с C# ДотНетом(ДотКором) мы зависим от него всегда.
Остались еще и в линукс такие - Gentoo! :)
А в случае с дотнетом можно тоже собрать один исполнимый файл, либы будут внутри.
Ага, можно правда его вес 50 или 60 мегабайт минимум будет (цифры для линукс). Так называемый selfhosted :)
Так ведь речь-то не об этом. Да, размер софта увеличился и 10мб для ехе - это уже замечательно. Однако, тут идет речь о ДотНете, и о том, что ехе на C# можно уменьшить до 1кб. Но это - ложь. Тот ехе, о котором идет речь в посте - не является самодостаточным исполнительным файлом и для его работы требуется дополнительный набор библиотек, без которых другие исполнительные файлы (конкретно ехе), могут работать.
ДотНет делает всю работу этого ехе, а в ехе находится только конечный код, словно в скриптовом файле
Я об этом уже писал где-то ниже (или выше). .NET всегда принуждает зависеть от него (если не пихать всё статически). И его нужно ставить на каждую платформу для работы такого ехе (еще и нужной версии). Внешние библиотеки - это либо API ОС, либо сторонние либы, где от либ ОС зависят абсолютно все программы (и это логично и понятно), а от сторонних библиотек зависеть не обязательно. В отличие от C# с .NET, который требуется установить в целевую ОС или таскать эти либы всегда с собой или статически линковать.
.NET - это ещё один уровень зависимостей. Гигабайты библиотек разных версий.
Чем хуже винда?
Тем что в ней нет apt-get! И нет, ответ "зато есть WinGet" не принимается, потому что его нет в Windows Server. А ещё WinGet не умеет в зависимости.
Но ведь и apt-get собственно есть не в каждом линуксе. Есть дистрибутивы, где в принципе никакого пакетного менеджера.
Так что я просто скажу про choco, который умеет в зависимости и его можно установить на windows server
Было бы интересно посмотреть на минимальный файл при компиляции с учётом тэга "self-contained". То есть с тем чтобы все необходимые библиотеки были запакованы в сам экзешник.
О, я примерно так же развлекался 18 лет назад. Только это был компилятор Delphi, Windows XP и программа выводила текущее системное время в диалоговом окне. А размер получился такой же, в районе 840 байт.
А я примерно так же развлекался году этак в 95-м, писал самый маленькую по размеру программу, которая перезапустит комп. Дольше всего было сокращение с 8 до 7 байт. Конечно, COM файл.
Ну, COM с PE не совсем честно сравнивать)
С перезагрузкой я развлекался при помощи shutdown и опций для отложенной перезагрузки, когда по очереди за одним компом работаешь. Ещё с автозапуском на CD тоже весёлые приколы были.
5 байт было. Я не помню, откуда я взял этот бинарник, но он весил пять байт. Что-то вроде F0 FF FE FF FF. При дизассемблировании вылезало jmp непонятно куда.
Потом мне уже попадалось версия с 2 байтами, сейчас поискал: "fa f4"cli ; disable all maskable interrupts
hlt ; stop the processor until an interrupt or hard reset happens
Но ведь это завесит компьтер, а не перезагрузит его. Да и завесит, вероятно, не все IBM PC-compatible машины. Остается ведь немаскируемое прерывание NMI, а его по-разному в разных клонах задействовали. Где-то ошибки чётности ловили, где-то на таймер заводили. Вероятно и другие варианты были.
Погуглил, "EA0000FFFF" JMP FFFF:0000
"When the computer boots, the x86 microprocessor starts in real mode and executes the instruction at FFFF:0000"
Хммм... Похоже, и у меня было не 7, а 5 байт.
У меня было 314: https://habr.com/ru/articles/276371/
А если каким-нибудь UPX еще это дело обработать? Или не возьмет?
Вот они, реинкарнации ушедших душ из fido.demo.design, даешь 256 байт + 64К интро, процедурные текстуры и real time ray raytracing
Их сравнивать некорректно.
Компилятор раста и .NET Runtime делают принципиально разные вещи.
Вот как минимум ряд вещей, которых в расте изначально нет by design, но они есть в .NET Runtime:
JIT-компилятор
Рефлексия
Сборка мусора
System.Reflection.Emit и всё что с ним связано - генерация кода в рантайме
Динамическая загрузка библиотек (часто используется для реализации всяких плагинов для программы)
(Может ещё что-то забыл)
В несколько раз меньше вести
Скорее на несколько порядков меньше весит :)
На счёт "в несколько раз быстрее" - это зависит от конкретного сценария. Вполне могут быть случаи из реального мира, где разница не такая значительная.
aot не совсем то что надо - это не избавляет от необходимости наличия жирного рантайма и сборки мусора, которая находится в фундаменте .net, Rust этим принципиально отличается - у него нет никакого рантайма и автоматической сборки мусора с отслеживанием достижимости объектов.
Что вам сейчас мешает некоторые участки кода "полноценно оптимизировать"?
Уже же и так есть unsafe, stackalloc, span&memory, AOT, unmamanged memory, и даже ключевые слова, чтобы контролировать время жизни параметров функций, почти как в Rust.
Что мешает сделать этот самый гибрид на основе Rust?
Сборку мусора можно втащить через crossbeam::epoch, рефлексия эмулируется при помощи макросов, jit, кодогенерацию, и подгрузку произвольного платформонезависимого кода можно легко реализовать через wasm.
Ну и ещё как нечто среднее между C# и Rust можно считать Go:
Есть сборка мусора, есть рефлексия, но при этом полностью AOT.
Верно ли понимаю, что раст фактически делает всё то, что делает .net
не совсем - Rust использует достаточно уникальную модель владения (owning) и одалживания (borrowing) сущностей и следит за этим на этапе компиляции, чтобы не нарушались правила. Если все нормально, компилятор сам где надо вставляет методы drop, очищающие память после использования, т.е. фактически вызов C++ деструкторов или free из Си. Ну и да, нет никакого рантайма - практически используется ABI от С/С++, т.к. внутри все преобразуется в машинный код с использованием LLVM
Спасибо, увлекательно!
Сейчас ещё интересно такое проделать с бинарями, в которые компилирует дотнет с включенным AOT - ну то есть когда он выдает сразу нативный код, а не IL. По последним тестам у такого хелловролда на шарпе размер аж 1.2 мегабайта, есть простор для оптимизаций.

До последнего байта: минимальный вариант Hello World для .NET