Bob wants to send a secret message to Alice using public key cryptography. Which of the following actions should Bob take?
Anonymous Quiz
10%
Use his own public key to encrypt the message and send it to Alice
8%
Use Alice's private key to encrypt the message and send it to Alice
31%
Use his own private key to encrypt the message and send it to Alice
51%
Use Alice's public key to encrypt the message and send it to Alice
🆒5
Заключительный пост по FrozenDictionary
Вместо короткого заключительного поста про FrozenDictionary, я решил переработать существующий материал и написать полноценную статью. Тем более, я не видел ни в рунете, ни в англоязычном интернете таких подробных разборов.
Я доработал бенчмарки, добавил поясняющие диаграммы, обновил графики. Вроде получилось интересно и познавательно.
Сегодня публикую на своём сайте. Завтра выйдет на Хабре. В ближайшие дни займусь переводом.
Приглашаю к чтению.
Вместо короткого заключительного поста про FrozenDictionary, я решил переработать существующий материал и написать полноценную статью. Тем более, я не видел ни в рунете, ни в англоязычном интернете таких подробных разборов.
Я доработал бенчмарки, добавил поясняющие диаграммы, обновил графики. Вроде получилось интересно и познавательно.
Сегодня публикую на своём сайте. Завтра выйдет на Хабре. В ближайшие дни займусь переводом.
Приглашаю к чтению.
🆒7
Быстрый расчёт остатка от деления
В процессе работы над статьёй про FrozenDictionary заметил немало интересных деталей, о которых хочется рассказать. Начнём с простого, поэтому сегодня о быстром алгоритме расчёта остатка от деления.
Как известно, индекс бакета в словаре рассчитывается как остаток от деления
В общем случае, эта операция включает в себя деление, которое выполняется медленнее, чем другие арифметические операции. Поэтому была придумана следующая формула:
Идея этого подхода в том, что если количество бакетов известно заранее, то можно вычислить значение
В FrozenDictionary и Dictionary используется другой вариант формулы, который, по словам разработчиков Microsoft немного быстрее, что подтверждается результатами бенчмарка (рисунок 1):
Кроме того, JIT-компилятор способен оптимизировать код и заменить деление на несколько операций умножения и битовый сдвиг, если значение
Код бенчмарка и результаты тут.
В процессе работы над статьёй про FrozenDictionary заметил немало интересных деталей, о которых хочется рассказать. Начнём с простого, поэтому сегодня о быстром алгоритме расчёта остатка от деления.
Как известно, индекс бакета в словаре рассчитывается как остаток от деления
hashCode на bucketsCount. В C# и многих других языках программирования для этого используется оператор %:var bucketNum = hashCode % bucketsCount;
В общем случае, эта операция включает в себя деление, которое выполняется медленнее, чем другие арифметические операции. Поэтому была придумана следующая формула:
uint FastModLemier(
uint hashcode,
ulong multiplier,
uint bucketsNum) =>
(uint)((((UInt128)(hashcode
* multiplier))
* bucketsNum) >> 64);
Идея этого подхода в том, что если количество бакетов известно заранее, то можно вычислить значение
multiplier по формуле ulong.MaxValue / bucketsNum + 1. Это позволяет заменить деление на умножение и битовые сдвиги. Если интересны подробности и математическое обоснование формулы, то можете ознакомиться со статьёй Дэниела Лемира, разработчика данного метода. Для общего понимания достаточно прочитать главы 1 и 3.2.В FrozenDictionary и Dictionary используется другой вариант формулы, который, по словам разработчиков Microsoft немного быстрее, что подтверждается результатами бенчмарка (рисунок 1):
uint FastModDotNet(
uint hashcode,
ulong multiplier,
uint bucketsNum) =>
(uint)(((((multiplier
* hashcode) >> 32)
+ 1) * bucketsNum) >> 32);
Кроме того, JIT-компилятор способен оптимизировать код и заменить деление на несколько операций умножения и битовый сдвиг, если значение
bucketsCount известно на этапе компиляции. В результате ASM-код для DefaultModConst будет идентичен коду FastModDotNet (рисунок 2).Код бенчмарка и результаты тут.
👍12
Создание массивов в C#: способы и производительность
Как создать массив в C#? Вопрос кажется простым. Но с каждой новой версией .NET появляются всё новые и новые фичи. На данный момент я знаю аж 7 способов создания массивов:
1. new T[]. Самый очевидный, простой и популярный способ создания объектов и массивов в частности. Этот оператор используется повсеместно и является основным для большинства сценариев.
2. stackalloc T[]. Выделяет память в стеке для массива заданного размера и возвращает указатель. Когда-то давно оператор
Для небольших массивов
3. ArrayPool<T>. ArrayPool выделяет массив из пула, что минимизирует количество аллокаций памяти и уменьшает нагрузку на сборщик мусора. Массивы, возвращаемые из пула, могут быть больше запрошенного размера, например, для массива из 700 элементов будет возвращён массив длиной 1024. Использование пула полезно, для относительно больших временных массивов, которые часто создаются и уничтожаются.
4. GC.AllocateUninitializedArray<T>. Этот метод создаёт массив без инициализации его элементов значениями по умолчанию. Использование этого метода может быть небезопасным, так как массив содержит неинициализированные данные. Обязательно перезаписывайте все значения в созданном массиве.
GC.AllocateUninitializedArray<T> быстрее
5. Array.Initialize. Позволяет создавать массивы, задавая тип во время выполнения. Например, когда тип неизвестен на этапе компиляции. Полезно для сценариев, связанных с динамической типизацией.
Производительность аналогична
6. InlineArrayAttribute. Относительно новая фича, появившаяся в C# 12. Позволяет объявить структуру с атрибутом InlineArray, которая будет вести себя как массив. Память для массива выделяется в стеке со всеми вытекающими ограничениями.
Быстрее
7. collections expressions. Синтаксический сахар, который позволяет записать массив в виде литерала. Например,
создаёт массив в куче, аналогично использованию
создаёт массив в стеке, аналогично
Код бенчмарка и результаты тут.
Как создать массив в C#? Вопрос кажется простым. Но с каждой новой версией .NET появляются всё новые и новые фичи. На данный момент я знаю аж 7 способов создания массивов:
1. new T[]. Самый очевидный, простой и популярный способ создания объектов и массивов в частности. Этот оператор используется повсеместно и является основным для большинства сценариев.
2. stackalloc T[]. Выделяет память в стеке для массива заданного размера и возвращает указатель. Когда-то давно оператор
stackalloc требовал использования небезопасного (unsafe) контекста. С появлением Span<T> его можно применять и без unsafe. Этот способ ограничен размером доступной памяти в стеке и при необдуманном использовании может привести к StackOverflowException.Для небольших массивов
stackalloc T[] быстрее new T[] в среднем на 20% и на 88% для больших массивов. Под небольшими массивами я подразумеваю массивы размером меньше 85 000 байт, т.е. размещаемые в куче малых объектов (SOH), а под большими – в куче больших объектов (LOH).3. ArrayPool<T>. ArrayPool выделяет массив из пула, что минимизирует количество аллокаций памяти и уменьшает нагрузку на сборщик мусора. Массивы, возвращаемые из пула, могут быть больше запрошенного размера, например, для массива из 700 элементов будет возвращён массив длиной 1024. Использование пула полезно, для относительно больших временных массивов, которые часто создаются и уничтожаются.
ArrayPool<T> быстрее new T[] в среднем на 30% для небольших массивов и на 89% для больших массивов. Однако для массивов размером менее 300–400 байт использование пула может быть медленнее.4. GC.AllocateUninitializedArray<T>. Этот метод создаёт массив без инициализации его элементов значениями по умолчанию. Использование этого метода может быть небезопасным, так как массив содержит неинициализированные данные. Обязательно перезаписывайте все значения в созданном массиве.
GC.AllocateUninitializedArray<T> быстрее
new T[] в среднем на 15% для массивов 2048 – 85 000 байт. В остальных случаях производительность с new T[] одинаковая.5. Array.Initialize. Позволяет создавать массивы, задавая тип во время выполнения. Например, когда тип неизвестен на этапе компиляции. Полезно для сценариев, связанных с динамической типизацией.
Производительность аналогична
new T[], либо хуже.6. InlineArrayAttribute. Относительно новая фича, появившаяся в C# 12. Позволяет объявить структуру с атрибутом InlineArray, которая будет вести себя как массив. Память для массива выделяется в стеке со всеми вытекающими ограничениями.
Быстрее
new T[] в среднем на 39% для небольших массивов и на 91% для больших массивов.7. collections expressions. Синтаксический сахар, который позволяет записать массив в виде литерала. Например,
int[] array = [1, 2, 3]
создаёт массив в куче, аналогично использованию
new T[]. А конструкцияSpan<int> array = [1, 2, 3]
создаёт массив в стеке, аналогично
stackalloc.Код бенчмарка и результаты тут.
👍16
alexeyfv
Как в FrozenDictionary подсчитываются коллизии хэша
Второй короткий пост о деталях реализации FrozenDictionary, которые остались за кадром. Сегодня об алгоритме подсчёта количества коллизий хэша.
Как в FrozenDictionary подсчитываются коллизии хэша
Второй короткий пост о деталях реализации FrozenDictionary, которые остались за кадром. Сегодня об алгоритме подсчёта количества коллизий хэша.
Второй короткий пост о деталях реализации FrozenDictionary, которые остались за кадром. Сегодня об алгоритме подсчёта количества коллизий хэша.
👍6
Как ошибки в бенчмарке могут привести к неправильным выводам
Видел я однажды пост в LinkedIn, заголовок которого утверждал, что .NET 9 медленнее, чем .NET 8. Сильное заявление. Проверять я его конечно буду. Ведь я сам большой любитель замеров производительности. Перейдём сразу к тому, что не так с бенчмарком.
❌ Методы не возвращают результат
Современные компиляторы умные. Они могут понять, когда выполняемый код не влияет на результат программы, и просто удаляют его. Такая оптимизация называется dead-code elimination (DCE). В данном случае, метод
✅ Всегда возвращайте результат из методов
Исправленный вариант может выглядеть так. Нам неважно, что будет в переменной sum. Главное, что её использование предотвращает DCE и не оказывает значительного влияния на результаты бенчмарка.
❌ Слепое сравнивание foreach
Как мы знаем,
- foreach с массивом будет преобразован в while c индексатором;
- foreach со списком – в while с List<int>.Enumerator;
- foreach с ICollection<int> – в while с IEnumerator<int>.
✅ Сравнивать производительность foreach нужно с разными типами
Поскольку
❌ Передача метода в виде параметра
При передаче метода в виде параметра создаётся делегат. В нашем примере –
✅ При передаче метода в виде параметра нужно заранее создать экземпляр делегата
Правильнее было бы проинициализировать делегат в методе
Выводы
Теперь посмотрим на результаты исправленного бенчмарка. Bad – изначальный бенчмарк, Better – после исправления.
В бенчмарке Bad, который я запустил на своём ноутбуке, метод
В бенчмарке Better, метод
В целом, результаты во всех трёх версиях .NET кажутся плюс-минус одинаковыми. Поэтому, я бы не стал заявлять о том, что .NET 9 медленнее .NET 8.
Видел я однажды пост в LinkedIn, заголовок которого утверждал, что .NET 9 медленнее, чем .NET 8. Сильное заявление. Проверять я его конечно буду. Ведь я сам большой любитель замеров производительности. Перейдём сразу к тому, что не так с бенчмарком.
❌ Методы не возвращают результат
Современные компиляторы умные. Они могут понять, когда выполняемый код не влияет на результат программы, и просто удаляют его. Такая оптимизация называется dead-code elimination (DCE). В данном случае, метод
DoSomeThing фактически не делает ничего, поэтому компилятор удаляет его в методе For. Но в методе ForEach_Linq такое сделать нельзя, т.к. в нём создаётся делегат. В результате получается сравнение методов с разным поведением (рисунок 2).✅ Всегда возвращайте результат из методов
Исправленный вариант может выглядеть так. Нам неважно, что будет в переменной sum. Главное, что её использование предотвращает DCE и не оказывает значительного влияния на результаты бенчмарка.
❌ Слепое сравнивание foreach
Как мы знаем,
foreach предназначен для итерации по коллекциям, которые возвращают Enumerator. Но даже если коллекция возвращает Enumerator, это не гарантирует, что он будет использоваться. Рассмотрим пример на рисунке 3:- foreach с массивом будет преобразован в while c индексатором;
- foreach со списком – в while с List<int>.Enumerator;
- foreach с ICollection<int> – в while с IEnumerator<int>.
✅ Сравнивать производительность foreach нужно с разными типами
Поскольку
foreach компилируется в различный код в зависимости от коллекции, производительность может значительно отличаться. Поэтому важно сравнивать каждый тип отдельно. Не исключаю, что эта деталь реализации может измениться в будущем.❌ Передача метода в виде параметра
При передаче метода в виде параметра создаётся делегат. В нашем примере –
Action<int> (рисунок 4). Делегат, как известно ссылочный тип, то есть его создание – аллокация памяти, которая влияет на результаты бечмарка.✅ При передаче метода в виде параметра нужно заранее создать экземпляр делегата
Правильнее было бы проинициализировать делегат в методе
GlobalSetup, который выполняется перед бенчмарками.Выводы
Теперь посмотрим на результаты исправленного бенчмарка. Bad – изначальный бенчмарк, Better – после исправления.
В бенчмарке Bad, который я запустил на своём ноутбуке, метод
ForEach_LinqParallel отработал на .NET 9 чуть быстрее, чем на .NET 8. То есть та разница в, о которой говорил автор, была в рамках погрешности.В бенчмарке Better, метод
ForEach_LinqParallel отработал на .NET 9 медленнее чем на .NET 8 на 90 мкс. Такую разницу я бы тоже отнёс к погрешности и не стал акцентировать внимание.В целом, результаты во всех трёх версиях .NET кажутся плюс-минус одинаковыми. Поэтому, я бы не стал заявлять о том, что .NET 9 медленнее .NET 8.
👍9
Soft Layoffs, или как сократить персонал с минимальными потерями для бизнеса
16 сентября CEO Amazon объявил, что с 2025 года все сотрудники компании будут работать 5 дней в неделю из офиса:
Amazon – не единственная компания, которая изменила подход к удалённой работе. Например, 18 сентября появилась новость, что теперь работники Ubisoft обязаны работать 3 дн в неделю из офиса. В июле Salesforce объявляла о необходимости работать в офиса 4-5 дней в неделю. Таких примеров можно найти множество.
Почти все такие новости подаются как забота об уникальной корпоративной культуре, но за этим скрывается решение проблемы с раздутым штатом и стремление сохранить прибыль. Есть мнение, что во время пандемии было нанято много новых сотрудников для удовлетворения возросшего спроса на IT-услуги, но с её окончанием столько работников уже не требуется. Встал вопрос, как с ними расстаться. Один из таких методов — Soft Layoffs, или мягкие увольнения.
Мягкие увольнения происходят уже довольно продолжительное время. В феврале этого года я публиковал перевод истории с Reddit, где IT-специалист из США рассказывал о том, как его вынуждают вернуться в офис, хотя он не хотел этого. Суть Soft Layoffs заключается в том, чтобы создать условия, при которых сотрудник уволится сам, что позволяет компании сократить расходы.
Способы мягких увольнений
Помимо введения обязательной работы в офисе, существуют и другие способы вынудить уволиться по-собственному:
- Заморозка найма с распределением новой нагрузки на существующих работников. Это приводит к переработкам, выгоранию и поиску новой работы.
- Урезание бенефитов: отмена корпоративов, ухудшение условий или полная отмена ДМС, замена зернового кофе на 3 в 1, а печенек на армейские галеты.
- Отмена индексации зарплат, квартальных и годовых премий.
Это основные методы, которые пришли мне в голову, но, вероятно, существуют и другие.
Преимущества для работодателей
Термин Soft Layoffs пришёл с Запада и я не знаю подробностей их трудового законодательства. Но наверняка там, как и в России, сокращение — сложная для работодателя процедура со множеством нюансов и ограничений. Проще договориться об увольнении по соглашению сторон или, если работодатель редиска, то вынудить уволиться по собственному желанию. Это позволяет:
- Разгрузить административный персонал, который не будет заниматься подготовкой документов для сокращения по ТК.
- Сократить расходы на выходные пособия, ведь при увольнении по собственному желанию они не выплачиваются.
- Избавиться от тех, кого уволить сложно уволить, например, беременных женщин или одиноких родителей с маленькими детьми.
- Минимизировать репутационные потери. «Компания X сокращает 10 000 человек» звучит хуже, чем «Компания X возвращает работников в офис».
Что в таких ситуациях делать работникам?
Работникам всегда нужно заботиться о своих интересах. Интересами работодателя пусть занимается сам работодатель. Конкретные советы, например, закрепление условий работы в коллективном договоре, я упоминал в майском посте про сокращение, под которое попал в 2022 году.
Если с поиском новой работы проблемы, а коллективного договора нет, можно применить стратегию quite quitting. Это когда сотрудник работает ровно столько, сколько требуют его должностные обязанности – ни больше, ни меньше. Он перестаёт перерабатывать, не стремится к повышению, не берёт на себя дополнительные задачи и не проявляет инициативу.
16 сентября CEO Amazon объявил, что с 2025 года все сотрудники компании будут работать 5 дней в неделю из офиса:
Our culture is unique <...>. But, keeping your culture strong is not a birthright. You have to work at it all the time. <...> the number of people we’ve hired the last 6-8 years to pursue these endeavors, it’s pretty unusual—and will stretch even the strongest of cultures.
<...> some of our teammates may have set up their personal lives in such a way that returning to the office consistently five days per week will require some adjustments. To help ensure a smooth transition, we’re going to make this new expectation active on January 2, 2025.
Amazon – не единственная компания, которая изменила подход к удалённой работе. Например, 18 сентября появилась новость, что теперь работники Ubisoft обязаны работать 3 дн в неделю из офиса. В июле Salesforce объявляла о необходимости работать в офиса 4-5 дней в неделю. Таких примеров можно найти множество.
Почти все такие новости подаются как забота об уникальной корпоративной культуре, но за этим скрывается решение проблемы с раздутым штатом и стремление сохранить прибыль. Есть мнение, что во время пандемии было нанято много новых сотрудников для удовлетворения возросшего спроса на IT-услуги, но с её окончанием столько работников уже не требуется. Встал вопрос, как с ними расстаться. Один из таких методов — Soft Layoffs, или мягкие увольнения.
Мягкие увольнения происходят уже довольно продолжительное время. В феврале этого года я публиковал перевод истории с Reddit, где IT-специалист из США рассказывал о том, как его вынуждают вернуться в офис, хотя он не хотел этого. Суть Soft Layoffs заключается в том, чтобы создать условия, при которых сотрудник уволится сам, что позволяет компании сократить расходы.
Способы мягких увольнений
Помимо введения обязательной работы в офисе, существуют и другие способы вынудить уволиться по-собственному:
- Заморозка найма с распределением новой нагрузки на существующих работников. Это приводит к переработкам, выгоранию и поиску новой работы.
- Урезание бенефитов: отмена корпоративов, ухудшение условий или полная отмена ДМС, замена зернового кофе на 3 в 1, а печенек на армейские галеты.
- Отмена индексации зарплат, квартальных и годовых премий.
Это основные методы, которые пришли мне в голову, но, вероятно, существуют и другие.
Преимущества для работодателей
Термин Soft Layoffs пришёл с Запада и я не знаю подробностей их трудового законодательства. Но наверняка там, как и в России, сокращение — сложная для работодателя процедура со множеством нюансов и ограничений. Проще договориться об увольнении по соглашению сторон или, если работодатель редиска, то вынудить уволиться по собственному желанию. Это позволяет:
- Разгрузить административный персонал, который не будет заниматься подготовкой документов для сокращения по ТК.
- Сократить расходы на выходные пособия, ведь при увольнении по собственному желанию они не выплачиваются.
- Избавиться от тех, кого уволить сложно уволить, например, беременных женщин или одиноких родителей с маленькими детьми.
- Минимизировать репутационные потери. «Компания X сокращает 10 000 человек» звучит хуже, чем «Компания X возвращает работников в офис».
Что в таких ситуациях делать работникам?
Работникам всегда нужно заботиться о своих интересах. Интересами работодателя пусть занимается сам работодатель. Конкретные советы, например, закрепление условий работы в коллективном договоре, я упоминал в майском посте про сокращение, под которое попал в 2022 году.
Если с поиском новой работы проблемы, а коллективного договора нет, можно применить стратегию quite quitting. Это когда сотрудник работает ровно столько, сколько требуют его должностные обязанности – ни больше, ни меньше. Он перестаёт перерабатывать, не стремится к повышению, не берёт на себя дополнительные задачи и не проявляет инициативу.
👍9
Как данные влияют на производительность
Хочу рассказать о том, насколько важен способ инициализации данных для достижения максимальной производительности кода. Рассмотрим следующий синтетический пример (см. рисунок):
- Массивы t1 и t2 содержат информацию о транзакциях.
- Два метода, Setup1 и Setup2, инициализируют массивы t1 и t2 соответственно: Setup1 – двумя циклами for, а Setup2 – одним.
- Метод Sum позволяет вычислить сумму транзакций в массивах t1 или t2.
Вопрос: при каком способе инициализации Sum(t1) + Sum(t2) выполнится быстрее? Голосуйте в опросе ниже. Правильный ответ с результатами бенчмарка и объяснением опубликую позднее.
Хочу рассказать о том, насколько важен способ инициализации данных для достижения максимальной производительности кода. Рассмотрим следующий синтетический пример (см. рисунок):
- Массивы t1 и t2 содержат информацию о транзакциях.
- Два метода, Setup1 и Setup2, инициализируют массивы t1 и t2 соответственно: Setup1 – двумя циклами for, а Setup2 – одним.
- Метод Sum позволяет вычислить сумму транзакций в массивах t1 или t2.
Вопрос: при каком способе инициализации Sum(t1) + Sum(t2) выполнится быстрее? Голосуйте в опросе ниже. Правильный ответ с результатами бенчмарка и объяснением опубликую позднее.
👍3
При каком способе инициализации Sum(t1) + Sum(t2) выполнится быстрее?
Final Results
10%
Результат будет одинаковый
38%
Setup2 — один цикл for
52%
Setup1 — два цикла for