Каждым резом надо разрезать каждый кусок пополам. 2 в 5 даст 32. Но вряд ли это можно осуществить не перекладывая кусков. Либо же резы должны быть не прямыми.
На моей памяти, бивикли всегда упоминался в значении 'раз в 2 недели'. Но там есть некоторая неоднозначность, выше уже упомянули.
А вот с бейсболом и регби в 16 пункте смешали коней с людьми. К слову, у американцев 'регби' это просто 'футбол' (а европейской футбол это 'соккер'), а 'регби' он как раз в UK. И это 2 разных игры на самом деле, если разобраться.
В вашей реализации TryGet используется именно он для удаления протухших ссылок.
Я не буду дальше с вами дискутировать, вижу мои доводы для вас не особо убедительны + я не в курсе всех особенностей вашей задачи.
Могу лишь предложить погонять ваше решение в условиях high memory pressure. Позамерять cache hit rate в различных сценариях. Собрать GC метрики при типичной нагрузке.
Смысл в том, что в вашей реализации с внешним локом, любая операция (даже чтение) будет блокировать ВСЮ коллекцию. В случае же с ConcurrentDictionary<T> (который, собственно, и используется в дефолтной реализации MemoryCache) для вставки/удаления блокируется только один конкретный бакет, в котором в случае с хорошей хеш-функцией живет один единственный элемент. И даже для них в некоторых случаях используются атомарные операции вместо полноценного лока. Я уже молчу про операцию чтения, которая там вообще неблокирующая.
От того, что ссылки на ключи и значения будут храниться дважды, потребление памяти вырастет процентов так на 5-10.
Согласен, тут я некорректно выразился. Я скорее имел в виду число аллокаций. Что при интенсивной нагрузке и довольно долгом (по меркам GC) времени жизни объектов в кеше с большой долей вероятности приведет к их скоплению во 2 поколении и его фрагментации. Но тут я не могу утверждать на 100% - надо делать профилирование. Я уверен, что разработчики, имплементировавшие MemoryCache, делалали ее, причем для разных workload сценариев. Делали ли его вы?
Зато для моей задачи критична стабильность отклика.
Поэтому и не стоит зря аллоцировать лишнюю память. Благо GC сейчас очень многое прощает, не зря его полировали годами, но все же перенапрягать его не стоит.
Конечно, в моей реализации тоже есть недостаток
Да, вот он: list.Remove(entry);
Вот его сложность - O(n), в случае же с Dictionary (и Concurrent в том числе), в общем случае это O(1). И только лишь в случае полного вырождения O(n).
Но это случается не так часто, как чистка.
Чистка в MemoryCache производится в отдельном фоновом потоке, причем с определенным каденсом, а не при каждом чихе, так что никаких фризов быть не должно. Если вы конечно не под одноядерный/однопоточный чип пишете...
Вы почему-то топите исключительно за универсальные решения
Это не так, я топлю за проверенные решения. Выстраданные и полирующиеся годами, которыми, на данном этапе, учитывая зрелость фреймворка, осмелюсь предположить, являются очень многие базовые классы. Так же, многие из них оптимизированы с учетом нюансов работы CLR, коих ни вы, ни я, ни многие другие разработчики "со стороны" всех не знают.
Мой совет - подумайте как перенести блокировки внутрь реализации. Перепишите с использованием ConcurrentDictionary. Кстати, на Хабре есть очень годная статья про него https://habr.com/ru/company/skbkontur/blog/348508/. Еще для этого придется выкинуть LinkedList, но может оно и к лучшему, зачем он тут вообще, все равно вы каждый раз ищете entry в словаре? Проверять протухла ли WeakReference можно и при обращении к ключу. Если боитесть что stale records будут жрать память - добавте асинхронную проверку/очистку фоновом потоке. Только тогда получится что вы написали свою урощенную версию MemoryCache.
Я же написал, почему MemoryCache меня не устраивает.
Мне не нужен expiration ни по времени, ни по занимаемой памяти, а только по факту существования ссылки на кэшируемый объект.
Ваш критерий это и есть "по занимаемой памяти", только в случае с MemoryCache он сам заботится о том чтобы не распухнуть, и трекает время последнего обращения, чтобы очистить самые редко используемые записи, так что с ним вам и WeakReferences не особо будут нужны. Главное не создавать записи с опцией NeverRemove, и все будет хорошо.
А что со скоростью и потокобезопасностью вашего велосипеда? Он покрыт тестами? Вы уверены что в вашей реализации нет утечек, собственно? Что будет если оно будет жить месяц без рестарта? А если в него закинуть 100 ГБ данных? А если туда приедет 10 миллиардов уникальных элементов?
Я не сомневаюсь что вы реализовали простое и рабочее решение. И абсолютно четко понимаю, что полно случаев, когда это оправданно. Просто зачастую в существующих реализациях зачастую продуманы/протестированы очень многие edge cases, которые можно (осознанно либо же нет) пропустить в своей. Я всего лишь топлю за то, чтобы это был обоснованный выбор, а не просто потому что было лень разбираться с существующим решением либо просто очень хотелось свой родной величек выкатить в прод.
Т.е. вы написали свой велосипед для кэширования. Не проще ли было использовать стандартный MemoryCache из библиотеки, который умеет ресайз и экспайрить элементы (например по размеру кэша или по таймауту), но при этом хранить в нем WeakRef вместо обычных ссылок. В таком случае нужна будет всего лишь одна доп. проверка при получении ссылки, чтобы убедиться что она не протухла.
Задачки на один зуб для тех кто хоть раз держал в руках сборник «Математические головоломки и развлечения» от Мартина Гарднера. Отличная штука для развития навыков счета, логики, пространственного мышления и способности нестандартно взглянуть на вещи.
Выглядит здорово! Единственное сомнение — насчет звукопоглощающих характеристик. Есть подозрение что он будет резонировать как рояль при появлении малейшего шума в вертушках… Но я могу и заблуждаться — в аккустике не силен.
Эхх, когда же уже будет нормальный нейроинтерфейс, хотя бы для набора текста, а не вот эти вот все костыли с ходунками, будь они мембранные, механические, или еще какие...
PrintScreen под правой рукой. Это лишний перенос руки на клавиатуру и обратно. Особенно критично, учитывая то, что вам нужно будет выделить область мышью после нажатия хоткея. (Конечно, если у вас ладонь как у Air Jordan, то скорее всего вы можете и принт скрин нажать левой, но все равно не слишком удобно)
Только что глянул на 4 ноутбучных клавиатуры, присутствующих в доме, и на 3 от ПК, - только на 2х клавиатурах логитек расположение клавиши PrintScreen совпадает. Win, Shift и S(creenshot) при этом, +/- в одном и том же месте.
Так что, думаю, UX спецы из МС все же не зря свой хлеб едят.
Самая большая беда в том (на мой взгляд), что все эти ИИ прелести в текущем исполнении требуют стабильного сетевого подключения, т.к. все обрабатывается в облаке. Соответственно, для реального мобильного приложения, к сожалению, они слабо применимы. Да и с юридической точки зрения это не всегда возможно - слать все подряд данные в другую страну на обработку.
Просто любопытно, какой смысл от бессмертия/пренебрежимого старения в мире где нечего есть/пить, нечем дышать, и какой-нибудь маньяк при власти вечно норовит выдернуть чеку из ядерной гранаты...
Разработка: накидал код a = b, протестировал что a = b на нескольких кейсах, проверил через год — a = b до сих пор.
Менеджмент: настроил процесс a = b, завтра оказалось что процесс не работает если b = 5, через неделю что точное сравнение вообще не уместно, а через две — что a — это число, b — строка, а сравнивать по понедельникам и средам вообще нельзя.
Ну да, практически одно и то же...
Да и сама война в большинстве случаев не война армий, а война экономик.
Нет, просто у айтишников развито логическое мышление, и мы склонны продумывать самые разные варианты развития событий, профессия обязывает.
Ну байнари же.
Каждым резом надо разрезать каждый кусок пополам. 2 в 5 даст 32. Но вряд ли это можно осуществить не перекладывая кусков. Либо же резы должны быть не прямыми.
На моей памяти, бивикли всегда упоминался в значении 'раз в 2 недели'. Но там есть некоторая неоднозначность, выше уже упомянули.
А вот с бейсболом и регби в 16 пункте смешали коней с людьми. К слову, у американцев 'регби' это просто 'футбол' (а европейской футбол это 'соккер'), а 'регби' он как раз в UK. И это 2 разных игры на самом деле, если разобраться.
Потому что https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/compmod/system/collections/generic/linkedlist.cs#L256
В вашей реализации TryGet используется именно он для удаления протухших ссылок.
Я не буду дальше с вами дискутировать, вижу мои доводы для вас не особо убедительны + я не в курсе всех особенностей вашей задачи.
Могу лишь предложить погонять ваше решение в условиях high memory pressure. Позамерять cache hit rate в различных сценариях. Собрать GC метрики при типичной нагрузке.
Ок, давайте по порядку.
Смысл в том, что в вашей реализации с внешним локом, любая операция (даже чтение) будет блокировать ВСЮ коллекцию. В случае же с ConcurrentDictionary<T> (который, собственно, и используется в дефолтной реализации MemoryCache) для вставки/удаления блокируется только один конкретный бакет, в котором в случае с хорошей хеш-функцией живет один единственный элемент. И даже для них в некоторых случаях используются атомарные операции вместо полноценного лока. Я уже молчу про операцию чтения, которая там вообще неблокирующая.
Согласен, тут я некорректно выразился. Я скорее имел в виду число аллокаций. Что при интенсивной нагрузке и довольно долгом (по меркам GC) времени жизни объектов в кеше с большой долей вероятности приведет к их скоплению во 2 поколении и его фрагментации. Но тут я не могу утверждать на 100% - надо делать профилирование. Я уверен, что разработчики, имплементировавшие MemoryCache, делалали ее, причем для разных workload сценариев. Делали ли его вы?
Поэтому и не стоит зря аллоцировать лишнюю память. Благо GC сейчас очень многое прощает, не зря его полировали годами, но все же перенапрягать его не стоит.
Да, вот он:
list.Remove(entry);Вот его сложность - O(n), в случае же с Dictionary (и Concurrent в том числе), в общем случае это O(1). И только лишь в случае полного вырождения O(n).
Чистка в MemoryCache производится в отдельном фоновом потоке, причем с определенным каденсом, а не при каждом чихе, так что никаких фризов быть не должно. Если вы конечно не под одноядерный/однопоточный чип пишете...
Это не так, я топлю за проверенные решения. Выстраданные и полирующиеся годами, которыми, на данном этапе, учитывая зрелость фреймворка, осмелюсь предположить, являются очень многие базовые классы. Так же, многие из них оптимизированы с учетом нюансов работы CLR, коих ни вы, ни я, ни многие другие разработчики "со стороны" всех не знают.
Мой совет - подумайте как перенести блокировки внутрь реализации. Перепишите с использованием ConcurrentDictionary. Кстати, на Хабре есть очень годная статья про него https://habr.com/ru/company/skbkontur/blog/348508/. Еще для этого придется выкинуть LinkedList, но может оно и к лучшему, зачем он тут вообще, все равно вы каждый раз ищете entry в словаре? Проверять протухла ли WeakReference можно и при обращении к ключу. Если боитесть что stale records будут жрать память - добавте асинхронную проверку/очистку фоновом потоке. Только тогда получится что вы написали свою урощенную версию MemoryCache.
Чтобы сэкономить время, можете сходить по ссылке https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs и посмотреть как оно все рализовано. Не знаю, можно ли переиспользовать код... Думаю да, с определенными оговорками - там вроде Apache 2.0
Или просто взять готовую реализацию MemoryCache.
Ваш критерий это и есть "по занимаемой памяти", только в случае с MemoryCache он сам заботится о том чтобы не распухнуть, и трекает время последнего обращения, чтобы очистить самые редко используемые записи, так что с ним вам и WeakReferences не особо будут нужны. Главное не создавать записи с опцией NeverRemove, и все будет хорошо.
Ну т.е. ее нет. Плюс к этому удвоенные требования по памяти... Очень "разумно" для кеша. Как раз то, о чем я и говорил.
А что со скоростью и потокобезопасностью вашего велосипеда? Он покрыт тестами? Вы уверены что в вашей реализации нет утечек, собственно? Что будет если оно будет жить месяц без рестарта? А если в него закинуть 100 ГБ данных? А если туда приедет 10 миллиардов уникальных элементов?
Я не сомневаюсь что вы реализовали простое и рабочее решение. И абсолютно четко понимаю, что полно случаев, когда это оправданно. Просто зачастую в существующих реализациях зачастую продуманы/протестированы очень многие edge cases, которые можно (осознанно либо же нет) пропустить в своей. Я всего лишь топлю за то, чтобы это был обоснованный выбор, а не просто потому что было лень разбираться с существующим решением либо просто очень хотелось свой родной величек выкатить в прод.
Т.е. вы написали свой велосипед для кэширования. Не проще ли было использовать стандартный MemoryCache из библиотеки, который умеет ресайз и экспайрить элементы (например по размеру кэша или по таймауту), но при этом хранить в нем WeakRef вместо обычных ссылок. В таком случае нужна будет всего лишь одна доп. проверка при получении ссылки, чтобы убедиться что она не протухла.
Извините, но перевод ужасный. Не смог читать дальше 3 абзаца.
Грудное кормление тоже не является противопоказанием для вакцинации Pfizer/AstraZeneca. Младенца привить нельзя, но мать можно. Было бы...
Задачки на один зуб для тех кто хоть раз держал в руках сборник «Математические головоломки и развлечения» от Мартина Гарднера. Отличная штука для развития навыков счета, логики, пространственного мышления и способности нестандартно взглянуть на вещи.
Выглядит здорово! Единственное сомнение — насчет звукопоглощающих характеристик. Есть подозрение что он будет резонировать как рояль при появлении малейшего шума в вертушках… Но я могу и заблуждаться — в аккустике не силен.
Я знаю только то, что ничего не знаю...
Эхх, когда же уже будет нормальный нейроинтерфейс, хотя бы для набора текста, а не вот эти вот все костыли с ходунками, будь они мембранные, механические, или еще какие...
PrintScreen под правой рукой. Это лишний перенос руки на клавиатуру и обратно. Особенно критично, учитывая то, что вам нужно будет выделить область мышью после нажатия хоткея. (Конечно, если у вас ладонь как у Air Jordan, то скорее всего вы можете и принт скрин нажать левой, но все равно не слишком удобно)
Только что глянул на 4 ноутбучных клавиатуры, присутствующих в доме, и на 3 от ПК, - только на 2х клавиатурах логитек расположение клавиши PrintScreen совпадает. Win, Shift и S(creenshot) при этом, +/- в одном и том же месте.
Так что, думаю, UX спецы из МС все же не зря свой хлеб едят.
Самая большая беда в том (на мой взгляд), что все эти ИИ прелести в текущем исполнении требуют стабильного сетевого подключения, т.к. все обрабатывается в облаке. Соответственно, для реального мобильного приложения, к сожалению, они слабо применимы. Да и с юридической точки зрения это не всегда возможно - слать все подряд данные в другую страну на обработку.
Просто любопытно, какой смысл от бессмертия/пренебрежимого старения в мире где нечего есть/пить, нечем дышать, и какой-нибудь маньяк при власти вечно норовит выдернуть чеку из ядерной гранаты...
Разработка: накидал код a = b, протестировал что a = b на нескольких кейсах, проверил через год — a = b до сих пор.
Менеджмент: настроил процесс a = b, завтра оказалось что процесс не работает если b = 5, через неделю что точное сравнение вообще не уместно, а через две — что a — это число, b — строка, а сравнивать по понедельникам и средам вообще нельзя.
Ну да, практически одно и то же...