Тут полностью согласен, но, увы, "качество поддержки" никак не учитывается. Опять же, ни в какой, самой "демократической" избирательной системе.
Потому что учитывать "качество" - это вводить заведомо неравенство по весу голосов. По каким признакам? Доход? Уровень образования?
Увы, но тут сложно (если вообще возможно) какой-то критерий сформулировать, такой, чтобы он был справедливым для всех. Вот и получается что власть опирается на общую (достаточно серую) массу, а не на наиболее развитых ее представителей.
Мы с вами живем в достаточно изолированном мирке. В кругу близких по духу людей. И нам кажется что все такие. А это не так. Нас с вами не так много (как хотелось бы). А попробуйте пообщаться с какими-нибудь работягами на каком-нибудь заводе на Урала, в Сибири... У которых в последний год-два зарплата "вдруг" выросла. Попробуйте их настроения понять, че они живут... И еще момент - это те люди, которые максимально подвержены действию пропаганды. Просто в силу своего менталитета.
Увы, но в среднем картина очень сильно отличается от той, что мы видим в своем окружении. Так что 86 не 86, но больше 50-ти запросто по стране в целом.
Ну и стоит иметь ввиду, что результаты выборов в значительной степени решаются до выборов. На этапе формирования списка кандидатов. И не только тут, но везде.
Увы, но приходится принимать реальность такой, какая она есть. Или не принимать и изменять ее конкретно для себя, коль нет возможности изменить ее для всех.
Очередь обычно делается кольцевым буфером, не linked list. Исключение – когда объекты уже лежат где-то и вы их просто связываете прямо на месте.
Кольцевые буфера хороши там, где поток сообщений относительно невелик, а ресурсы сильно ограничены. Например, на промконтроллере.
А вот когда у вас ядро, обеспечивающее связь десятком контроллеров с несколькими интерфейсными клиентами, да еще количество контроллеров (а значит плотность потока сообщений) может варьироваться в широких пределах, тут уже кольцевые буфера сильно неудобны. Хотя бы тем, что вам придется сразу выделить ну очень много памяти в статике.
Помножьте на то, что размер сообщения может быть разным - от десятков байт до килобайт. В статическом буфере вам придется выделять память под максимальный размер сообщения.
Помножьте на то, что могут быть "приоритетные" сообщения, которые помещаются не в конец, а в начало очереди.
А еще сообщение может не получить подтверждения и его нужно передвинуть в очереди на перепосылку через какое-то время...
У поверьте, я с этим много наелся когда занимался разработкой ядра системы мониторинга инженерного оборудования зданий. Начинали с буферов, но достаточно быстро наелись с этим и перешли на очереди. С которыми все стало намного проще.
Сейчас задачи другие, но вот есть параллельная обработка больших объемов данных (десятки и сотни элементов). И там есть такая штука как "конвейер". Тоже на FIFO очередях строится.
Play-off – тот же один проход по массиву (двумя указателями) удаляет всё лишнее за O(n), как и в случае linked list.
Play-Off, как следует из названия, подразумевает многократный проход по списку с удалением элементов на каждом проходе.
В общих чертах - пришло некоторое условие. По нему составляется список элементов, удовлетворяющих этому условию (ну для понимания - 500-600 тысяч элементов). Приходит второе условие - проходим по списку и удаляем все, что не проходит по второму условию. Потом третье, пятое, десятое... Каждый раз список сокращается. Поскольку элементы физически удаляются, то каждый новый проход будет короче предыдущего. С массивом тоже можно, но там придется или два массива делать с перебросом из одного в другой и обратно (менять их местами на каждом проходе), или с одним, но с копированием последнего элемента массива на место очередного выбывшего. Что тоже требует времени.
В результате по скорости (подтверждено PEX статистиками) разницы нет - что связанный список, что два массива, что один. А вот простоте и понятности кода со списком намного проще.
Простейший случай - идете по цепочке от начала к концу и для каждого элемента принимаете решение - оставить его или удалить. Такой вот play-off алгоритм.
Есть еще очереди. Например, на отправку сообщений. Сообщение для отправки берется из начала очереди. Новые сообщения добавляются в конец. Отправленное (точнее, отправленное на которое получено подтверждение о поиеме) сообщение удаляется из начала очереди.
Свои реализации делаются или когда что-то очень специфическое по данным, или когда готовой нет в наличии (такое тоже бывает в жизни), или когда нужна предельная эффективность вкупе с какой-то спецификой задачи и приспособить готовое получается достаточно коряво.
Список или массив... Нам часто приходится иметь дело с очень большими объемами данных. Память в целом не проблема, можно считать что она не ограничена. Но есть ограничение - 16Мб одним куском (ну можно 2Тб, но там уже приходится "танцевать вприсядку" - это другая модель памяти и, в общем случае, даже другой размер указателя). И вот если нет уверенности что получится выделить массив (даже динамический массив все равно в памяти одним куском будет), то тогда используется список.
Там еще есть варианты - списки ключ-данные с необходимостью поиска по ключу. Можно организовать это массивом, отсортировать его и потом использовать быстрый двоичный поиск (быстрее не бывает). Но тут есть момент - если у нас данные постоянно меняются, то каждый раз пересортировывать массив из хотя бы сотни тысяч элементов (а для нас это не сказать чтобы сильно много) приводит просто к катастрофической потере производительности. И вот тут лучше список ключ-данные.
А если это статика - один раз заполнил, отсортировал и потом 100500 раз ищешь - там сортированный массив по производительности лучше.
Если мне приходится делать реализации списков, я обычно сразу закладываю туда счетчик элементов. Если реализован двусвязный список, то любой элемент по номеру можно получить не более чем за length / 2 шагов - если n <= length / 2 - считаем с начала списка, иначе - с конца.
Вообще, двусвязный список предпочитаю делать кольцевым с двумя типами узлов.
Один узел имеет тип HEAD и содержит счетчик элементов и ссылки на первый и последний элементы списка (при необходимости там же могут быть какие-то другие свойства списка). Остальные элементы имеют тип NODE и содержат блок данных и ссылки на следующий и предыдущий элементы (первый в качестве предыдущего ссылается на HEAD, последний, в качестве следующего, аналогично ссылается на HEAD.
Можно и 100гб к каждому дому. Но дальше все это сводится на концентратор от которого потребуется уже 1тб на 10 домов...
Проблема ше не в канале от дома до провпйдера, а в том сколько сам аровайдер может через себя пропустить.
И да. Я в частном доме за городом. И да, оптика в дом заведена. 100мб (тариф) честных (реально 95-97 в обе стороны) вполе хватает на 4 точки iptv и на работу (аудио-видео коммуникации). Хотя при необходимости провпйдер до 500мб предлагает тарифы.
И оптика жто не только пропускная спсобность, но и грозоустойчивость, например
Вот только приплетать сюда IBM z (и можно еще IBM i) до кучи совсем ни к чему. Это Hi-End серверы для бизнеса. Высокопроизводительные и высоконадежные.
Почитайте как устроена IBM i (Фрэнк Солтис - Основы AS/400) - ну нет в Linux ничего подобного. И не будет никогда.
А что касается чипов... Современные процессоры настолько дороги в производстве (от разработки до корпусирования), что все это рентабельно только в очень больших объемах. Больших, чем объемы рынка одной отдельно взятой страны. Так что тут пытаться сделать что-то свое и только для себя заранее обречено на провал.
Я с мейнфреймами не работал. Которые нынче IBM z, но работаю с middleware платформой IBM i (бывшая AS/400). Так вот там COBOL до сих пор поддерживается. Т.е. перенести все написанные на COBOL приложения со старого сервера на новый - никаких проблем.
Более того, архитектура этой системы подразумевает хранение в бинарнике не только собственно исполняемого кода, но и промежуточного TIMI (Technology Independant Machine Instructions) кода. И метки под какой процессор сгенерирован исполняемый бинарный код. При переносе на новый сервер (процессор) при первом запуске система обнаружит что исполняемый код не под тот процессор и автоматически перегенерирует его из TIMI.
Там есть ограничения. Про кобол не знаю, мы с RPG работаетм. Там тоже есть поддержка типов с фиксированной точкой. Так вот тип packed (packed decimal - фактически BCD) ограничен 63-мя знаками всего и 31-м знаком после запятой. Т.е. максимально можно объявить
Пример программы на COBOL — это обычный Hello World
Интересно, а сколько людей из тех, кто тут пишет про кобол и мейнфреймы, написали хоть одну программу на нем для этих самых мейнфреймов...
Но ситуация постепенно меняется. Корпорация IBM разработала набор инструментов, позволяющий портировать COBOL-программы на Java — Watsonx Code Assistant.
Рекламы много, вот только не видел ни одного восторженного отзыва типа "Вау!!! Перевели с кобола на джаву - он сразу стало быстрее работать и при этом меньше ресурсов потреблять - смогли заменить один дорогой мейнфрейм на пару дешевых мелких серверов".
Ну вообще я привык что когда что-то делается, оно делается под конкретные сценарии использования. А не "вааще шоб было".
Пример сценария я привел - работа с БД (поддержка типа DECIMAL в БД) и финансовые расчеты. Возможно, есть еще какие-то сценарии с которыми я лично не сталкивался.
Но если понятны сценарии, тогда можно уже рисовать как удобные для потенциальных сценариев API, так и эффективную в рамках данного сценария реализацию.
И да, мне хочется. Не то чтобы хочется, мне это необходимо чтобы (как писал выше) не иметь потенциальных проблем с клиентами банка (репутационные риски, жалобы и риски нежелательного внимания со стороны регулятора, штрафы и т.п.) Но, слава богу, у меня все это есть - я (мы) работаю с языком, где поддержка такой арифметики нативна - я языке есть соответствующие типы и все операции с ними (и в реализации С/С++ на этой платформе есть _Decimal для С и _DecimalT<n,p> для С++). И там да, переполнение всегда вызывает системное исключение, которое можно легко перехватить и обработать (а не обработаешь - ну упадет, оставив дампы и записи в joblog - будет "дефект промсреды").
если вы хорошо разбирайтесь в теме - давайте ссылки где написано, что этот Decimal реализован через BCD
Я в этой систем работаю. IBM i.
Чтобы понять как это работает, достаточно посмотреть что такое BCD и что такое packed decimal у IBM (это тот самый DECIMAL в SQL, _DecimalT<> в C++) Улавливаете сходство (мягко говоря)?
Современные процессоры обычную двоичную арифметику, тоже поддерживают, кстати
Речь о том, для современного процессора вы можете написать реализацию, которая будет работать с фиксированной точкой (BCD) без конвертаций туда-сюда, на уровне инструкций процессора.
Т.е. не эмуляцию, а полноценную реализацию. Что и реализовано у IBM (правда, мы не с x86 работаем, а с рисковыми Power процессорами - подозреваю, что там возможности процессора по работе с BCD побогаче). Т.е. там нет каких-то специальных библиотек. Все это поддерживается на уровне "машинных инструкций" (низкоуровневые примитивные команды)
Самое главное, какая производительность этого дела? Сравнима ли она с double, float?
Производительность в каких приложениях?
Все это делается не для производительности. А, например, для повышения устойчивости к накоплению ошибок округления. Для этого есть специальные тесты
Второй аспект - работа с БД. Там есть типы DECIMAL и NUMERIC. С фиксированной точкой. Широко используются в финансовых расчетах.
Если вам удастся хорошо реализовать "нативную" арифметику с этими типами, то производительность "на круг" будет выше - вам не потребуется каждый раз при чтении из БД конвертировать эти типы в плавающую точку, потом заморачиваться с ошибками округления, а потом конвертировать обратно чтобы записать в БД.
Т.е. вся эта история делается под конкретные сценарии.
Тут полностью согласен, но, увы, "качество поддержки" никак не учитывается. Опять же, ни в какой, самой "демократической" избирательной системе.
Потому что учитывать "качество" - это вводить заведомо неравенство по весу голосов. По каким признакам? Доход? Уровень образования?
Увы, но тут сложно (если вообще возможно) какой-то критерий сформулировать, такой, чтобы он был справедливым для всех. Вот и получается что власть опирается на общую (достаточно серую) массу, а не на наиболее развитых ее представителей.
А сколько есть? Вы точно знаете? Как посчитали?
Мы с вами живем в достаточно изолированном мирке. В кругу близких по духу людей. И нам кажется что все такие. А это не так. Нас с вами не так много (как хотелось бы). А попробуйте пообщаться с какими-нибудь работягами на каком-нибудь заводе на Урала, в Сибири... У которых в последний год-два зарплата "вдруг" выросла. Попробуйте их настроения понять, че они живут... И еще момент - это те люди, которые максимально подвержены действию пропаганды. Просто в силу своего менталитета.
Увы, но в среднем картина очень сильно отличается от той, что мы видим в своем окружении. Так что 86 не 86, но больше 50-ти запросто по стране в целом.
Ну и стоит иметь ввиду, что результаты выборов в значительной степени решаются до выборов. На этапе формирования списка кандидатов. И не только тут, но везде.
Увы, но приходится принимать реальность такой, какая она есть. Или не принимать и изменять ее конкретно для себя, коль нет возможности изменить ее для всех.
Проверка списка на "зацикливание". Классически решается через "быстрый" и "медленный" указатели.
Кольцевые буфера хороши там, где поток сообщений относительно невелик, а ресурсы сильно ограничены. Например, на промконтроллере.
А вот когда у вас ядро, обеспечивающее связь десятком контроллеров с несколькими интерфейсными клиентами, да еще количество контроллеров (а значит плотность потока сообщений) может варьироваться в широких пределах, тут уже кольцевые буфера сильно неудобны. Хотя бы тем, что вам придется сразу выделить ну очень много памяти в статике.
Помножьте на то, что размер сообщения может быть разным - от десятков байт до килобайт. В статическом буфере вам придется выделять память под максимальный размер сообщения.
Помножьте на то, что могут быть "приоритетные" сообщения, которые помещаются не в конец, а в начало очереди.
А еще сообщение может не получить подтверждения и его нужно передвинуть в очереди на перепосылку через какое-то время...
У поверьте, я с этим много наелся когда занимался разработкой ядра системы мониторинга инженерного оборудования зданий. Начинали с буферов, но достаточно быстро наелись с этим и перешли на очереди. С которыми все стало намного проще.
Сейчас задачи другие, но вот есть параллельная обработка больших объемов данных (десятки и сотни элементов). И там есть такая штука как "конвейер". Тоже на FIFO очередях строится.
Play-Off, как следует из названия, подразумевает многократный проход по списку с удалением элементов на каждом проходе.
В общих чертах - пришло некоторое условие. По нему составляется список элементов, удовлетворяющих этому условию (ну для понимания - 500-600 тысяч элементов). Приходит второе условие - проходим по списку и удаляем все, что не проходит по второму условию. Потом третье, пятое, десятое... Каждый раз список сокращается. Поскольку элементы физически удаляются, то каждый новый проход будет короче предыдущего. С массивом тоже можно, но там придется или два массива делать с перебросом из одного в другой и обратно (менять их местами на каждом проходе), или с одним, но с копированием последнего элемента массива на место очередного выбывшего. Что тоже требует времени.
В результате по скорости (подтверждено PEX статистиками) разницы нет - что связанный список, что два массива, что один. А вот простоте и понятности кода со списком намного проще.
Вопрос - а что были за задачи в практике?
Простейший случай - идете по цепочке от начала к концу и для каждого элемента принимаете решение - оставить его или удалить. Такой вот play-off алгоритм.
Есть еще очереди. Например, на отправку сообщений. Сообщение для отправки берется из начала очереди. Новые сообщения добавляются в конец. Отправленное (точнее, отправленное на которое получено подтверждение о поиеме) сообщение удаляется из начала очереди.
Например, в ситуациях, когда по какой-причине нужно вставлять или удалять элементы внутри цепочки.
Свои реализации делаются или когда что-то очень специфическое по данным, или когда готовой нет в наличии (такое тоже бывает в жизни), или когда нужна предельная эффективность вкупе с какой-то спецификой задачи и приспособить готовое получается достаточно коряво.
Список или массив... Нам часто приходится иметь дело с очень большими объемами данных. Память в целом не проблема, можно считать что она не ограничена. Но есть ограничение - 16Мб одним куском (ну можно 2Тб, но там уже приходится "танцевать вприсядку" - это другая модель памяти и, в общем случае, даже другой размер указателя). И вот если нет уверенности что получится выделить массив (даже динамический массив все равно в памяти одним куском будет), то тогда используется список.
Там еще есть варианты - списки ключ-данные с необходимостью поиска по ключу. Можно организовать это массивом, отсортировать его и потом использовать быстрый двоичный поиск (быстрее не бывает). Но тут есть момент - если у нас данные постоянно меняются, то каждый раз пересортировывать массив из хотя бы сотни тысяч элементов (а для нас это не сказать чтобы сильно много) приводит просто к катастрофической потере производительности. И вот тут лучше список ключ-данные.
А если это статика - один раз заполнил, отсортировал и потом 100500 раз ищешь - там сортированный массив по производительности лучше.
Да, конечно от задачи. Иногда вообще ничего не нужно - можно использовать готовые библиотечные реализации.
Если мне приходится делать реализации списков, я обычно сразу закладываю туда счетчик элементов. Если реализован двусвязный список, то любой элемент по номеру можно получить не более чем за length / 2 шагов - если n <= length / 2 - считаем с начала списка, иначе - с конца.
Вообще, двусвязный список предпочитаю делать кольцевым с двумя типами узлов.
Один узел имеет тип HEAD и содержит счетчик элементов и ссылки на первый и последний элементы списка (при необходимости там же могут быть какие-то другие свойства списка). Остальные элементы имеют тип NODE и содержат блок данных и ссылки на следующий и предыдущий элементы (первый в качестве предыдущего ссылается на HEAD, последний, в качестве следующего, аналогично ссылается на HEAD.
Сам объект "список" - это ссылка на HEAD.
Можно и 100гб к каждому дому. Но дальше все это сводится на концентратор от которого потребуется уже 1тб на 10 домов...
Проблема ше не в канале от дома до провпйдера, а в том сколько сам аровайдер может через себя пропустить.
И да. Я в частном доме за городом. И да, оптика в дом заведена. 100мб (тариф) честных (реально 95-97 в обе стороны) вполе хватает на 4 точки iptv и на работу (аудио-видео коммуникации). Хотя при необходимости провпйдер до 500мб предлагает тарифы.
И оптика жто не только пропускная спсобность, но и грозоустойчивость, например
Вот только приплетать сюда IBM z (и можно еще IBM i) до кучи совсем ни к чему. Это Hi-End серверы для бизнеса. Высокопроизводительные и высоконадежные.
Почитайте как устроена IBM i (Фрэнк Солтис - Основы AS/400) - ну нет в Linux ничего подобного. И не будет никогда.
А что касается чипов... Современные процессоры настолько дороги в производстве (от разработки до корпусирования), что все это рентабельно только в очень больших объемах. Больших, чем объемы рынка одной отдельно взятой страны. Так что тут пытаться сделать что-то свое и только для себя заранее обречено на провал.
Ну насколько можно верить человеку, ни строчки на коболе не написавшему :-)
Смотреть надо спецификации. Но мне лень сейчас (по крайней мере) :-)
Я с мейнфреймами не работал. Которые нынче IBM z, но работаю с middleware платформой IBM i (бывшая AS/400). Так вот там COBOL до сих пор поддерживается. Т.е. перенести все написанные на COBOL приложения со старого сервера на новый - никаких проблем.
Более того, архитектура этой системы подразумевает хранение в бинарнике не только собственно исполняемого кода, но и промежуточного TIMI (Technology Independant Machine Instructions) кода. И метки под какой процессор сгенерирован исполняемый бинарный код. При переносе на новый сервер (процессор) при первом запуске система обнаружит что исполняемый код не под тот процессор и автоматически перегенерирует его из TIMI.
Там есть ограничения. Про кобол не знаю, мы с RPG работаетм. Там тоже есть поддержка типов с фиксированной точкой. Так вот тип packed (packed decimal - фактически BCD) ограничен 63-мя знаками всего и 31-м знаком после запятой. Т.е. максимально можно объявить
packed(63:31)
Это тип, который соответствует SQL типу DECIMAL
Интересно, а сколько людей из тех, кто тут пишет про кобол и мейнфреймы, написали хоть одну программу на нем для этих самых мейнфреймов...
Рекламы много, вот только не видел ни одного восторженного отзыва типа "Вау!!! Перевели с кобола на джаву - он сразу стало быстрее работать и при этом меньше ресурсов потреблять - смогли заменить один дорогой мейнфрейм на пару дешевых мелких серверов".
Тут не про строки, тут про то, как сознательно убиться головой об стену.
100% защиты от дурака не существует - найдет лазейку и любую обойдет.
Ну вообще я привык что когда что-то делается, оно делается под конкретные сценарии использования. А не "вааще шоб было".
Пример сценария я привел - работа с БД (поддержка типа DECIMAL в БД) и финансовые расчеты. Возможно, есть еще какие-то сценарии с которыми я лично не сталкивался.
Но если понятны сценарии, тогда можно уже рисовать как удобные для потенциальных сценариев API, так и эффективную в рамках данного сценария реализацию.
И да, мне хочется. Не то чтобы хочется, мне это необходимо чтобы (как писал выше) не иметь потенциальных проблем с клиентами банка (репутационные риски, жалобы и риски нежелательного внимания со стороны регулятора, штрафы и т.п.) Но, слава богу, у меня все это есть - я (мы) работаю с языком, где поддержка такой арифметики нативна - я языке есть соответствующие типы и все операции с ними (и в реализации С/С++ на этой платформе есть _Decimal для С и _DecimalT<n,p> для С++). И там да, переполнение всегда вызывает системное исключение, которое можно легко перехватить и обработать (а не обработаешь - ну упадет, оставив дампы и записи в joblog - будет "дефект промсреды").
Я в этой систем работаю. IBM i.
Чтобы понять как это работает, достаточно посмотреть что такое BCD и что такое packed decimal у IBM (это тот самый DECIMAL в SQL, _DecimalT<> в C++) Улавливаете сходство (мягко говоря)?
Речь о том, для современного процессора вы можете написать реализацию, которая будет работать с фиксированной точкой (BCD) без конвертаций туда-сюда, на уровне инструкций процессора.
Т.е. не эмуляцию, а полноценную реализацию. Что и реализовано у IBM (правда, мы не с x86 работаем, а с рисковыми Power процессорами - подозреваю, что там возможности процессора по работе с BCD побогаче). Т.е. там нет каких-то специальных библиотек. Все это поддерживается на уровне "машинных инструкций" (низкоуровневые примитивные команды)
Add Numeric (ADDN)
Subtract Numeric (SUBN)
Multiply (MULT)
Divide (DIV)
Там, кстати, еще есть полезные вещи - операции с округлением. Скажем, есть у вас есть два переменных:
A типа decimal(5.3)
и
B типа decimal(4,2)
Так вот, если A = 3.245, то присвоение B = A без округления даст B = 3.24, а присвоение с округлением - B = 3.25
Переполнение - нештатная ситуация. И может возникнуть вне зависимости от жирноты базового типа.
Вопрос в том - заметите вы его сразу, или потащите кривой результат дальше с видом что "так и надо"?
То, как это сделано в С++ не отмазка. Там много чего оставлено на усмотрение конечного разработчика.
Производительность в каких приложениях?
Все это делается не для производительности. А, например, для повышения устойчивости к накоплению ошибок округления. Для этого есть специальные тесты
Второй аспект - работа с БД. Там есть типы DECIMAL и NUMERIC. С фиксированной точкой. Широко используются в финансовых расчетах.
Если вам удастся хорошо реализовать "нативную" арифметику с этими типами, то производительность "на круг" будет выше - вам не потребуется каждый раз при чтении из БД конвертировать эти типы в плавающую точку, потом заморачиваться с ошибками округления, а потом конвертировать обратно чтобы записать в БД.
Т.е. вся эта история делается под конкретные сценарии.