Pull to refresh

Comments 73

Всё равно дочитал до конца - понравилось.
Всё равно дочитал до конца - понравилось.

UFO just landed and posted this here

Трактуйте это как предмет искусства.

Мне кажется, что грузить dll и дергать puts - это уже читерство. Вполне может оказаться что в одной из стандартных библиотек уже есть строка "Hello, world", например.

Да, так и есть. Я как раз пришёл пожаловаться. Я в такое количество бит мог уложить игру 2048 написанную на асьме. Но при том что я статически линковал все эти библиотеки, размер файла неизбежно выростал до 76 килобайт.

Плюсую нещадно.
Оптимизация лишней не бывает, и это относится к коду, бинарнику, базам, запросам.
В какой-то момент за неряшливый код приходится платить.
Да и эстетическое удовольствие - читать красивый код.

Такое скорее нужно именно вирусописателям, но никак не при нормальной инженерной практике. Хотя с познавательной точки зрения статья огонь!

Вот именно что с познавательной точки зрения.

Вирусописателям? Не дотнете? Серьезно?

Их полно. Вирус это не только что-то работающее в режиме ядра, - вирус может быть многокомпонентным, например взаимодействующим с сетью - а это поверьте удобнее на дотнете делать, скрывая процесс тем же драйвером ядра, или даже внедрять свой дотнет код в существующий процесс

Sweet summer child...
Даже на хабре помню, что была как минимум одна статья, где разбирался вирус на .NET

Мы когда то ботоводили в игре одной, а чтобы античит не спалил - все обертки были написаны на батниках, а взаимодействие - на autoit, никаких эксплоитов и "подозрительных" процессов. И много лет все работало.

Оптимизация лишней не бывает

Оптимизация всегда лишняя, пока не появилась потребность в этой самой оптимизации. Лучше простой, стабильный, понятный код, чем ускорение запуска на несколько миллисекунд.

Начинаем холивар )

Начинаем холивар )

Зачем?
Мне удовольствие, вам излишество. Уважаю ваше мнение, но остаюсь при своем.
За свободу мнений и вариативность!
Кто-то выше заминусил мой коммент. Мне не понятно, но если челу в кайф, ок. :)

Оптимизация всегда лишняя, пока не появилась потребность в этой самой оптимизации.

Лишняя оптимизация - такая, которая ничем не обоснована.
Есть такие случаи, где заранее известно, что что-то нужно, хотя момент, когда это необходимо ещё не наступил.

"Обоснована" - значит есть осмысленный ответ на вопрос "чтобы что?"

Статья и обсуждение "это нужно вирусописателям" напомнили давние рассказы программистов игровых консолей с жестко ограниченным количеством памяти, отсутствием виртуальной памяти и тп.

Вирусы сейчас тоже жирные ) Потому что грузят свое тело по сети. А такие хаки с уменьшением размера весьма вероятно триггернут антивирус.

Ну на дотнете обычно не пишут там где есть жесткие ограничения по памяти, учитывая вес самого рантайма и минимальный набор рабочего процесса в мегабайт эдак 20-30 :)

Странно, что в 2023-м году и net472.
Ну и да, троллейбус_из_хлеба.жпг

Потому что с .net core изначальный размер бинарника вырастает до ~150кб

А ещё про разработчиков на C++ говорят что они "байтослесари"...

Только вместо слесарей должно быть другое слово.

Если уж на то пошло, то оптимизацию лучше делать в сторону уменьшения потребления оперативки, hello world с.net framework472 занимает 1.5мб оперативки при 4кб бинарнике, с .net core чуть больше, 2.5мб

Шикарная статья. Всем, кто пишет про "зачем", предлагаю ознакомиться с жанром 64k intro и теми высотами, которых он достиг на современных графических API. Я к тому, что эта статья примерно из той же категории. Спасибо за перевод!

Только в данном случае, это равнозначно скрипту на питоне. Он тоже весит килобайты, а если получить байт-код - ещё меньше. Только от этого, он программой не становится

Они и не претендуют называться исполнительным файлом ехе. По сути, дотнет приложения, которые не слинкованы статически вообще могут использовать не ехе расширение, потому что смысла в нем нет, пока нет среды которая бы позволила работать "программе".

Согласен, расширение exe и загрузчик в начале файла тут только ради удобства запуска. Дотнет-сборкам вообще не особо нужно прикидываться PE-файлами.
Я про другое. .NET Framework, по крайней мере определённых версий, считается компонентом Windows. С некоторой натяжкой это в общем-то системное API. Равно как и Direct3D, например. Вопрос - где проходит граница между "программой" и "скриптом на питоне"?) Что должно/не должно быть в бинаре?

Раньше и так граница была размыта, а сейчас ещё больше. Но всё же есть некоторые зацепки. К примеру возьмем любой компилируемый язык с кроссплатформенными возможностями. В зависимости от реализации, мы можем получить либо отсутствие работы углубленно с API (самим фреймворком), т.е. без нативного интерфейса (или имитация), либо реализация нативного интерфейса для всех целевых платформ. Однако, в любом из этих случаев мы имеем зависимость только от самой ОС (на каждой из платформ), а не от чего-то, что было создано для этого языка.

Т.е. ДотНет - это часть именно Windows, а не Линукс или МакОС. Однако, "программа" на C# на Линукс всё ещё зависит именно от ДотНета (части Windows).

.NET Framework, по крайней мере определённых версий, считается компонентом Windows

Нюанс в том что в разных версиях Windows разная версия .NET Framework. А запускать софт, требующий другую версию, оно не может, надо ставить больше фреймворков.

Не забудьте посчитать размер самого дотнета. Или, слинкуйте статический "программу".

зачем? 99% программ в линуксе например использует те или иные библиотеки, которые качаются в систему целиком, нажимаешь apt-get install megaprogram И получаешь 200 зависимостей которые качаются к тебе на ПК. Чем хуже винда? Это или VS C++ либы, либо .NET. Учитывая что .NET являются частью любой современной системы MS (отдельно можно поставить и на старые), то для пишущего ПО это только плюс, а уж плюсы сетевой дистрибьюции какие.

Например у меня есть две утилиты консольные, обе 10240 байт EXE-шники, но с очень разным размером кода, одна на 3-4 страницы, а вторая на 15. То есть программа лежит видимо в минимальном однофайловом контейнере. Кстати обе утилиты работают в неизменном виде на линуксе тоже (а, не, вру. сейчас они под 4.8, а для кроссплатформенности я пересобирал их, кажется по 18 Кб становились, но уже работали в любой системе одинаково, удобно же, я под линукса писать совсем не умею, а тут тяп-ляп и готово).

Устанавливаемые зависимости из вашего примера не являются обязательными для каждой программы. Ты можешь их не использовать или использовать их в качестве исходников и собрать бинарь. А в случае с C# ДотНетом(ДотКором) мы зависим от него всегда.

ну лет 25 назад когда я пользовался FreeBSD я собирал каждую программу из исходников и действительно получал "чистый" бинарник. Но сегодня самые популярные дистрибутивы базируются на репозиторных сборках, где либы собраны и под них бинарники, поэтому проще 1 раз собрать либу и софт под неё, но да, качать при установке софта придется и софтину и либу. Так что ваш вариант в среде линукса практически неживой и рассматривать его нет смысла.

А в случае с дотнетом можно тоже собрать один исполнимый файл, либы будут внутри.

Остались еще и в линукс такие - Gentoo! :)

доля линукса 1-3%, какая там доля Gentoo? Что-то думается мне SUSE и RedHat "немного" больше (про попсовые дистрибутивы вообще молчу).

А в случае с дотнетом можно тоже собрать один исполнимый файл, либы будут внутри.

Ага, можно правда его вес 50 или 60 мегабайт минимум будет (цифры для линукс). Так называемый selfhosted :)

когда я в 2023 году скачиваю драйвер nvidia размером в 1 гиг или смотрю на телефоне размеры клиента сбера тоже в гиг, то я как-то не вспоминаю свою программу 1999 года, которая делала для радио медийные планы рекламы, воспроизводила музыку в эфире и занимала 400 кб. хотим мы того или не хотим, но весь софт увеличился в размерах КРАТНО. Я лично считаю это плохой тенденцией, но явно на это я повлиять не могу.

Из тех 50-60 мег о которых вы пишете это в основном библиотеки, интегрированные в файл, вы расплачиваетесь за "однофайловость", за отсутствие необходимости ставить что-то еще. Я наоборот собираю свои файлы опираясь на то, что в системе будет стоять нужная версия фрейморка. У меня всегда стоит. Файл 10240 байт всегда, пишу я что-то еще или нет. Видимо контейнер не может быть меньше этого размера. Знаю кучу софта из 90-00-х написанного под WinAPI, которые занимали единицы килобайт (будильники, часы, утилиты по расчёту сопротивлений, для работы с COM портом и т.п.).

Уже в 2001-2002 я на делфи писал программы которые использовали кучу DLL, тот же BDE, и весили уже сильно больше чем одна дискетка. Тогда стал выручать UPX.

Так что - селяви.

Так ведь речь-то не об этом. Да, размер софта увеличился и 10мб для ехе - это уже замечательно. Однако, тут идет речь о ДотНете, и о том, что ехе на C# можно уменьшить до 1кб. Но это - ложь. Тот ехе, о котором идет речь в посте - не является самодостаточным исполнительным файлом и для его работы требуется дополнительный набор библиотек, без которых другие исполнительные файлы (конкретно ехе), могут работать.
ДотНет делает всю работу этого ехе, а в ехе находится только конечный код, словно в скриптовом файле

я вас удивлю (возможно), но это касается не только .net, любой язык который умеет пользоваться внешними библиотеками так или иначе будет генерировать либо пухлые EXE файлы, либо худой экзешник с внешними либами. И такое было еще в 80-е, почему вы не в курсе-то?

Почему не поворчать про питон??? Там схема такая же. Си/СиПП, Паскаль.

Даже когда вы пишете на чистом ассемблере но используете WinAPI, вы точно так же пользуетесь тем, что лежит в виде внешних библиотек. Какая разница-то? Почему для .net это вдруг стало злом?

Я об этом уже писал где-то ниже (или выше). .NET всегда принуждает зависеть от него (если не пихать всё статически). И его нужно ставить на каждую платформу для работы такого ехе (еще и нужной версии). Внешние библиотеки - это либо API ОС, либо сторонние либы, где от либ ОС зависят абсолютно все программы (и это логично и понятно), а от сторонних библиотек зависеть не обязательно. В отличие от C# с .NET, который требуется установить в целевую ОС или таскать эти либы всегда с собой или статически линковать.

.NET - это ещё один уровень зависимостей. Гигабайты библиотек разных версий.

 .NET всегда принуждает зависеть от него

DirectX - всегда принуждает зависеть от него

CUDA - всегда принуждает зависеть от него

OpenGL - всегда принуждает зависеть от него

и т.д. до бесконечности

И его нужно ставить на каждую платформу для работы такого ехе

Бесконечный список можете смотреть выше по тексту.

(еще и нужной версии)

Внезапно первая в своем роде ) Вы точно не впервые попали в мир ПО и ОС?

Внешние библиотеки - это либо API ОС, либо сторонние либы, где от либ ОС зависят абсолютно все программы

Ну я понимаю если драйвер, который связывает железо и софт, да, он подпадает под ваше правило, но всё остальное - нет.

а от сторонних библиотек зависеть не обязательно

Все библиотеки - сторонние. Всегда. В этом их смысл.

В отличие от C# с .NET, который требуется установить в целевую ОС

То есть вас лично не устраивает единовременная установка .NET, но устраивает что в вашу систему весь остальной софт ставит и ставит и ставит библиотеки за библиотеками? Дак .NET - это же универсализация как раз.

.NET - это ещё один уровень зависимостей

Вместо 1000 зависимостей только одна.

У вас жизнь программиста не приходилась на 90-00-е года? А то складывается ощущение что вы телепортировались на Землю в 2019 году.

Но ведь и apt-get собственно есть не в каждом линуксе. Есть дистрибутивы, где в принципе никакого пакетного менеджера.

Так что я просто скажу про choco, который умеет в зависимости и его можно установить на windows server

Все верно - apt это пакетный менеджер debian и его производных (ubuntu, linux mint etc), в той же федоре и производных его нет но вместо него dnf, в arch based дистрибутивах - pacman вроде все основные перечислил :)

Было бы интересно посмотреть на минимальный файл при компиляции с учётом тэга "self-contained". То есть с тем чтобы все необходимые библиотеки были запакованы в сам экзешник.

Примерно так:
image


Все версии содержат одну единственную строку Console.WriteLine("Hello world"). Используется .NET 8, публикация под win-x64 в режиме self-contained (т.е. кроме экзешника ничего больше не нужно, сам рантайм не требуется).

О, я примерно так же развлекался 18 лет назад. Только это был компилятор Delphi, Windows XP и программа выводила текущее системное время в диалоговом окне. А размер получился такой же, в районе 840 байт.

А я примерно так же развлекался году этак в 95-м, писал самый маленькую по размеру программу, которая перезапустит комп. Дольше всего было сокращение с 8 до 7 байт. Конечно, COM файл.

Ну, COM с PE не совсем честно сравнивать)

С перезагрузкой я развлекался при помощи shutdown и опций для отложенной перезагрузки, когда по очереди за одним компом работаешь. Ещё с автозапуском на CD тоже весёлые приколы были.

Да я ж и не сравниваю. Я тоже прошел по цепочке от Паскаля (или Дельфи), через С++, С, ассемблер, до машинного кода. В 8 байтах я использовал стандартное прерывание, а в 7 байтах - какой-то малодокументированный переход по какому-то адресу. А потом запихнул это еще в свой TSR "под себя".

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 байт.

А если каким-нибудь UPX еще это дело обработать? Или не возьмет?

Вот они, реинкарнации ушедших душ из fido.demo.design, даешь 256 байт + 64К интро, процедурные текстуры и real time ray raytracing

Кстати, ради обсуждения, кто-нибудь понимает разницу между тем, как работает компилятор раста, и .net рантаймом? Верно ли понимаю, что раст фактически делает всё то, что делает .net, но на этапе компиляции + оперативки ест ~в 10-100 раз меньше, в несколько раз быстрее, и в несколько раз меньше весит?

Их сравнивать некорректно.

Компилятор раста и .NET Runtime делают принципиально разные вещи.

Вот как минимум ряд вещей, которых в расте изначально нет by design, но они есть в .NET Runtime:

  1. JIT-компилятор

  2. Рефлексия

  3. Сборка мусора

  4. System.Reflection.Emit и всё что с ним связано - генерация кода в рантайме

  5. Динамическая загрузка библиотек (часто используется для реализации всяких плагинов для программы)

(Может ещё что-то забыл)

В несколько раз меньше вести

Скорее на несколько порядков меньше весит :)

На счёт "в несколько раз быстрее" - это зависит от конкретного сценария. Вполне могут быть случаи из реального мира, где разница не такая значительная.

Гм, выходит из его плюсов вырастают и все его минусы. Совместить бы раст и шарп, чтобы получить прекрасный язык, и прекрасный компилятор...

А то сейчас слишком уж много весит и сам .net, и его рантайм. Да и производительность страдает :(

Я подозреваю, что не только я так думаю, не зря в последнее время .net идёт семимильными шагами в aot

aot не совсем то что надо - это не избавляет от необходимости наличия жирного рантайма и сборки мусора, которая находится в фундаменте .net, Rust этим принципиально отличается - у него нет никакого рантайма и автоматической сборки мусора с отслеживанием достижимости объектов.

Да вот я и надеюсь, что после aot появится какой-нибудь компилятор, который позаимствует у раста часть фишек. Или хотя бы даст возможность некоторые участки кода полноценно оптимизировать.

Что вам сейчас мешает некоторые участки кода "полноценно оптимизировать"?
Уже же и так есть 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

Тут скорее даже смысл в том, что изначально GC был добавлен для отслеживания утечек памяти, а раст всю эту кашу решает и без него. Поэтому .net тут явно проигрывает, но это пока не сильно заметно, раст ещё не на пике своей популярности.

В 17 году майки исследовали вопрос "Non-blocking safe manual memory management in .NET", но с тех пор что-то ничего не слышно

https://www.microsoft.com/en-us/research/publication/project-snowflake-non-blocking-safe-manual-memory-management-net/

а раст всю эту кашу решает и без него

Решает, да не всю.
1. Утечка памяти не считается UB, а по тому вполне возможна в Rust даже без unsafe.
2. Если у тебя есть циклические ссылки - тебе придётся включить unsafe и работать с сырыми указателями.

Серебреных пуль нет.

Спасибо, увлекательно!

Сейчас ещё интересно такое проделать с бинарями, в которые компилирует дотнет с включенным AOT - ну то есть когда он выдает сразу нативный код, а не IL. По последним тестам у такого хелловролда на шарпе размер аж 1.2 мегабайта, есть простор для оптимизаций.

Sign up to leave a comment.

Articles