Обновить
-3

Пользователь

0,2
Рейтинг
Отправить сообщение

начнут строго спрашивать за производительность

try-catch-finally/using практически бесплатны (если не помещать их внутри длинного цикла на горячем пути, конечно)

throw дорогой, но - можно пример сценария, при котором throw начинает влиять на производительность нормального/expected случая, а значит и среднюю производительность?

избегать большой вложенности вызовов (и большой глубины стека) - это тоже хороший стиль

Более чем спорно

Да, был не прав, задумался о своём.

Конечно же, и поля не даёт напрямую изменить, и сеттер для проперти вызвать.

Термин defensive copy применительно к не-ридонли структурам о чём-нибудь говорит?

Вообще-то - нет, не избегает. Ровно такое же поведение с т.з. defensive copy.

Важно, что LinkedList выделяет в куче индивидуальные ноды, тогда так List - массивы (с запасом).

Если списки не настолько большие, чтобы перейти в LOH, то начиная с определённого момента затраты на GC и "утрамбовку" памяти для списка могут стать гораздо дешевле, чем для связного списка.

Не знаю как в вашем случае, а для какого-нибудь хайлоада/хай срупута я бы предпочел список (если быть до конца точным - структуру, основанную на массивах и не допускающую попадания в ЛОХ)

На мой взгляд Zero-Alloc LINQ можно было бы реализовать 100% безопасным кодом

Есть опыт?

Я бы лично с удовольствием (и благодарностью) ознакомился, особенно под .НЕТ ниже седьмого.

Тут есть очень много "но".

Демонстрационный пример очень удачный, так как у нас простой фаст-трак/хэппи пас и несколько обработчиков исключительных ситуаций, куда мы выпадаем редко и передаём в них минимум параметров.

Теперь представим себе, что основная логика и она же - хэппи пас у нас реально объёмная. Она может быть простая, ветвлений минимум, фаст трак (проверки условий, после которых в 99% мы просто переходим на следующую инструкцию) - но объём метода в целом сотни строк, если не тысяча/чи.

Допустим, мы хотим сделать его менее врайт-онли и поделить на более короткие методы с ограниченными ответственностями. Ок, но теперь в каждый выделенный метод мы должны передавать весь необходимый контекст - что запросто может оказаться десятком параметров. А некоторые параметры у нас, к примеру, относительно объёмные структуры. Ок, передаём их по ссылке. Но, теперь у нас ещё и локальность данных размазалась по стеку.

Не уверен, что джит всё это заинлайнит и вернёт наш исходный фаст трак, даже если повесить на каждый метод агрессив инлайнинг.

Ну, как не уверен? Эмпирически знаю - не заинлайнит.

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

И ради чего? Читабельность? Это, конечно, хорошо, но не всегда стоит потерь производительности.

И это только первое из многих "но" :)

Я к чему? Без бенчмарков и профилирование на разных сценариях я лично никаких сложных и объёмных методов на горячем пути разделять не советовал бы.

...объединять, кстати, на моём опыте - менее рисковано.

Нет, массив всегда есть последовательный непрерывный блок памяти.

Оператор fixed просто запрещает GC перемещать блок памяти (на время своего действия).

А как ключевое слово - позволяет объявить инлайн-буфер заданной длины внутри unsafe-структуры.

Когда вы обрабатываете 10 байт это одно а когда 10 гигабайт это другое.

Когда 10 байт лежат рядом в текущей кэш-линии - это одно, а когда их нужно прочитать непосредственно из DRAM - это совсем другое.

Ключевое слово здесь "не нужен доступ по индексу". На первый взгляд можно бы и согласиться - добавление, вставка и удаление будут быстрее.

Но есть нюансы.

Про кэш-линии уже упомянули.

Но моё любимое - GC.
Мусора такой список создаёт слишком много, чтобы быть быстрым [на более-менее долгом промежутке времени].
Я лично бенчмаркал все стандартные не-массив-бейсед коллекции (включая LinkedList, SortedList и SortedSet), и могу сказать, что для хайлоада они слишком много мусорят. Те, что tree-based - даже при енумерации.

Для себя нашел выход: если действительно не нужен доступ по индексу (и байнари сёрча и т.п.) - лучший вариант это связанный список, но не создающий ноду в куче на каждый элемент, а использующий struct ноды в предварительно выделенных массивах. Да, есть оверхед для учёта и переиспользования свободных/освободившихся слотов в массивах нод, оверхэд на выделение большего массива и копирование данных при добавлении сверх текущей ёмкости, но в целом это гораздо лучший вариант для списков, которые живут дольше одной-двух сборок мусора.

Было бы очень любопытно узнать больше деталей.

Тоже писал некоторым-образом-виртуальную-машину, правда входом для неё был не код, а граф датафлоу. Было бы интересно сравнить опыт.

Хозяева решили закрыть принадлежащее им предприятие.

Ну, вчера хозяева, завтра - никто.
Доля в собственности не отменяет необходимости порядочно себя вести.

Ну, как-бы кидок, не?
После попытки кидка любая реакция считается в пределах нормы.

Технично. Всё правильно сделали.

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

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

...не понимаю праведного возмущения по поводу отжима. Как-бы вторая сторона сама сделала для этого всё возможное.

Собственно, к чему я и вёл.

Пропустил слово: одним из самых частых ОБРАБАТЫВАЕМЫХ кодов ошибки будет что-то вроде АНЭКСПЕКТЕД_РЕТУРН_КОД.

Имелось ввиду "любой".

Не, любопытно было чем этот подход кажется лучше исключений. В сравнении с го [мне] понятно в чём смысл.

Не хочу занудствовать, но теперь уже реально хочется понять.

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

Теперь я правильно понял?

Если речь об этом, то концептуально это одно и то же.

И проблема остаётся той же: мы не можем с уверенностью собрать все возможные типы ошибок так, чтобы ко всему прочему оно оставалось верным и во время исполнения. А значит в реальной жизни одним из самых частых кодов ошибки будет что-то вроде АНЭКСПЕКТЕД_РЕТУРН_КОД.

Ну и отдельный большой вопрос - почему нужно делать именно так, чем оно лучше?

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

Я понимаю, компилятор/ИДЕ/анализатор может находить некоторое подмножество возможных исключений и показывать это программисту/помогать обрабатывать.

В этом был бы смысл, если бы было возможно найти ВСЕ везможные исключения метода/функции. Но это невозможно, см. примеры Интерфейса/Абстрактного класса/Рантайма новой версии/Плагина.

Значит либо программисту всё равно придётся лезть в документацию сторонних библиотек/свой код - и даже это не поможет в случае обновления рантайма/сторонних библиотек на исполняющей машине, либо он будет оперировать неполной (==ложной) информацией.
Что в итоге даёт нам одинаковый результат.

Вывод: "нинада". Перефразируя: лови, что можешь исправить и будь, что будет.

Примерно такой у меня ход мысли на этот счёт.

для этого ничего, кроме документации АПИ не нужно.

  1. Да при чём тут хедер - скомпилировали с хедером версии 1.1, в рантайме доступна версия 1.2, на уровне сигнатур функций полностью совместимы, а список исключений изменился.

  2. Никто ничего не факапит. Усложним: Программа предоставляет плагину некоторое окружение, в том числе функции/проперти, есть списки исключений. Система скомпилирована, версия 1.1. Третья контора написала под эту версию плагин, плагин свободно продаётся. У пользователя система версии 1.2, плагин скомпилированный под версию 1.1. АПИ бинарно совместимы, список исключений системы версии 1.2 отличается.

    Что скомпилированный плагин может знать об исключениях рантайма новой версии?

Информация

В рейтинге
3 076-й
Зарегистрирован
Активность