Обновить

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

на то он и unsafe, чтобы работать без каких-либо проверок в рантайме. зато вы теперь знаете, во сколько обходятся все эти “безопасные” фишки в языке.

Какие фишки? Чем отличается от безопасного доступа к публичному полю?

может там под капотом геттер втихаря вызывается, а они виртуальные, и надо вначале найти правильный, это запрос к классу, где у него геттер поля, потом вызов, всё это обернуто в код, ловящий исключения, без дизасма вызова не сказать. А тут хоп и “получить память по смещению от базового указателя”, одна инструкция, кроме того объект, если мелкий, целиком в кэш прочитан, код даже в память не ходит лишний раз.

Это если есть геттер

С полем протестил клодом, получился доступ по смещению

// string token = Token(secret);
mov   rax, gword ptr [rcx+0x08] ret

// Token(secret) = “Изменено”; 
lea   rcx, bword ptr [rcx+0x08] 
call  CORINFO_HELP_ASSIGN_REF   // write barrier, т.к. поле ссылочное 
nop 
ret

Уходили от плюсов(и прочего "не безопасного"), костылями приделывают обратно. А как же парадигма ООП? Как же "если не должно быть видно, то прячем поглубже из публичности"?

Цель понятна ибо "надо", но путь по которому движется - стрёмен.

Способ из.NET 8 выглядит так. Метод объявлен как extern static, тела у него нет — его подставляет рантайм:

Точно в рантайме? Не смотрел реализацию, но выглядит как-то не оптимально в рантайме тратить время на генерацию кода.

Всякое бывает. Мне приходилось подменять статическую локальную функцию в библиотеке парсинга екселя, чтобы улучшить этот самый парсинг.

В плюсах по умолчанию всё опасно, а безопасность требует плясок с бубном.

В шарпе - наоборот. Так и было задумано, чтобы грязные хаки выглядели как грязные хаки и привлекали особое внимание.

Если оно по скорости такое же, как прямой доступ к полю, и работает в компайл тайме, то почему отсутствие поля с таким названием выбрасывает исключение, а не падает при компиляции? И какой IL оно подставляет, если передать несуществующее поле? Какое смещение? После конца класса, что ли?

Я так понял в момент первого вызова вычисляется смещение и jit либо генерит доступ к полю, либо генерит метод, бросающий исключение.

Поиск по имени проходит разово когда идет первая подготовка метода к выполнению. Не при компиляции.

Рантайм ищет поле по имени и не найдя бросает MissingFieldExceptionunsafeaccessors.cpp, строка 1146. Если найдет то сгенерит IL с обычным ldfld по смещению данного поля. Вариант вида «после конца класса» не существует прост не будет никакой генерации.

Компилятор проверить не может: имя задано строкой в атрибуте, а поле приватное и в какой сборке может быть сразу неизсетно.

А что за задача, где доли наносекунд играют роль? Это метод нужно миллиард раз дернуть и то даде секунды не будет.

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

Каждая оптимизация, очевидно, имеет накопительный эффект, и наоборот - каждая наивная "абы как"/"чистый код" имплементация тоже имеет накопительный эффект, но с противоположным знаком.

Лично наблюдал/участвовал в переписывании симуляционных движков, где время прогона снижалось сначала с суток-полутора-двух до двух-трёх часов, а потом и до часа/десятков минут.

...да даже UI - там где нужен действительно сложный/многоуровневый пересчёт подсказок/индикаторов/итого при редактировании полей и без заметного глазу лага.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации