惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
B
Blog
I
InfoQ
Engineering at Meta
Engineering at Meta
B
Blog RSS Feed
The Register - Security
The Register - Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
Blog — PlanetScale
Blog — PlanetScale
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
A
Arctic Wolf
Martin Fowler
Martin Fowler
C
Check Point Blog
C
Cisco Blogs
博客园 - 司徒正美
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
T
Tenable Blog
G
Google Developers Blog
量子位
阮一峰的网络日志
阮一峰的网络日志
有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
L
LINUX DO - 热门话题
Hugging Face - Blog
Hugging Face - Blog
IT之家
IT之家
T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
Security Latest
Security Latest
P
Privacy & Cybersecurity Law Blog
博客园 - 三生石上(FineUI控件)
G
GRAHAM CLULEY
Project Zero
Project Zero
V
Visual Studio Blog
Jina AI
Jina AI
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
T
Threatpost
Know Your Adversary
Know Your Adversary

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Вы можете победить бинарный поиск
Алексеев Бронислав · 2026-06-15 · via Все публикации подряд на Хабре

Вы можете победить бинарный поиск

Средний

6 мин

11

Иногда нам нужно найти значение в отсортированном массиве. Простейший алгоритм заключается в последовательном переборе значений, пока мы не встретим искомое значение или не достигнем конца массива. Такой алгоритм иногда называют линейным поиском. В C++ добиться такого же эффекта можно с помощью функции std::find.

Для больших массивов лучшего результата можно достичь с помощью бинарного поиска. Бинарный поиск является классическим алгоритмом, который эффективно находит целевое значение в отсортированном массиве, многократно деля интервал поиска пополам. Начиная со всего массива, он сравнивает целевое значение со средним элементом: если цель меньше, верхняя половина отбрасывается; если больше — отбрасывается нижняя. Этот процесс продолжается, пока цель не будет найдена или интервал не станет пустым. На больших наборах данных бинарный поиск значительно быстрее линейного. В C++ он реализован функцией std::binary_search, которая возвращает true или false в зависимости от существования элемента.

Популярный формат Roaring Bitmap использует массивы 16-битных целых чисел размером от 1 до 4096 элементов. Иногда нам приходится проверить, существует ли значение в этом массиве. Для этого мы используем бинарный поиск.

Я захотел создать более быстрый подход. У меня были две мысли:

  1. Практически все процессоры сегодня имеют инструкции для параллельной обработки данных (также называемая SIMD), которые могут проверять несколько значений за раз. И 64-битные ARM, и x64 процессоры (Intel/AMD) всегда поддерживают сравнение восьми 16-битных чисел с искомым значением используя всего одну инструкцию. Это подсказывает, что не стоит углубляться в бинарном поиске до блоков размером меньше восьми элементов. Кроме того, имеет смысл сравнивать шестнадцать элементов и больше.

  2. Бинарный поиск проверяет одно значение за раз. Однако, современные процессоры могут загружать и проверять более одного значения одновременно. Они обладают прекрасным параллелизмом на уровне памяти. Это говорит о том, что вместо бинарного поиска, стоит попробовать четверичный поиск: вместо того чтобы делить массив пополам, мы можем разделить их на четыре части. Конечный результат может потребовать чуть больше инструкций, но количество инструкций перестанет являться ограничивающим фактором.

Так я и создал алгоритм, который называю SIMD Quad. Это эффективный алгоритм поиска в отсортированных массивах с 16 битными беззнаковыми целыми числами, сочетающий четверичный интерполяционный поиск с SIMD (Single Instruction, Multiple Data). Алгоритм делит массив на блоки фиксированного размера по 16 элементов (возможно, за исключением последнего блока) и использует последний элемент каждого блока как интерполяционный ключ, чтобы быстро сузить область поиска до одного блока, а затем применяет SIMD-инструкции для одновременной проверки всех 16 элементов в этом блоке.

В основе лежит идея выполнения иерархического поиска: сначала используется интерполяционный поиск на более грубом уровне (по границам блоков), чтобы найти блок, содержащий целевое значение, а затем происходит переключение на SIMD для точной параллельной проверки внутри блока. Этот гибридный подход использует и сильные стороны алгоритмической оптимизации, так и аппаратного ускорения (SIMD проверяет несколько элементов за раз).

  1. Начальная проверка: если в массиве менее 16 элементов, выполняется простой линейный поиск

  2. Разбиение на блоки: массив делится на блоки по 16 последовательных элементов. Для массива размером cardinality существует num_blocks = cardinality / 16 полных блоков.

  3. Четверичный интерполяционный поиск: используем последний элемент каждого блока (на позициях 16-1, 32-1 и других) как ключи для интерполяции. Поиск выполняет четверичную (по основанию 4) интерполяцию, чтобы найти блок, в котором, вероятно, находится целевое значение. Это включает в себя сравнение цели с точками, делящими текущий диапазон поиска на четверти.

  4. Выбор блока: после сужения диапазона выбирается подходящий индекс блока lo на основе результатов интерполяции.

  5. SIMD-проверка: если найден верный блок, 16 элементов загружаются в SIMD-регистры (с использованием NEON на ARM или SSE2 на x64), после чего выполняются параллельные сравнения на равенство с целевым значением. Если найдено найдено хотя бы одно совпадание, возвращается true.

  6. Проверка остатка: для элементов, не вошедших в полные блоки (остаток) выполняется линейный поиск.

Каковы результаты этого? Я написал бенчмарк. Бенчмарк работает следующим образом: для каждого массива размером от 2 до 4096 элементов генерируется 100,000 сортированных массивов с 16-битными беззнаковыми целыми числами. Для каждого размера выполняется 10 миллионов запросов на принадлежность в «холодном» режиме (каждый запрос ищет в другом массиве, имитируя промахи кэша) и 10 миллионов запросов в «горячем» режиме (запросы сгруппированы по массивам, каждый массив обыскивается 100 раз подряд, имитируя попадания в кэш). Бенчмарк измеряет среднее время на один запрос для трёх алгоритмов: линейного поиска (std::find), бинарного поиска (std::binary_search) и нового алгоритма SIMD Quad.

Я использую две системы: Apple M4 с Apple LLVM и процессор Intel Emerald Rapids с GCC.

Во-первых, давайте сравним линейный и бинарный поиск.

Intel/GCC

Intel/GCC

Apple/LLVM

Apple/LLVM

Результат ясен. Бинарный поиск лучше чем линейный поиск, как только массивы становятся большими. Этого следовало ожидать.

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

Мы установили, что бинарный поиск является чистым победителем по линейному поиску. Давайте теперь сравним с алгоритмом SIMD Quad.

Intel/GCC

Intel/GCC

Apple/LLVM

Apple/LLVM

Результаты заметно различаются между Intel и Apple. На Intel SIMD Quad более чем вдвое быстрее бинарного поиска на «горячем» кэше. На «холодном» кэше преимущества меньше. На платформе Apple ситуация обратная: именно на «холодном» кэше SIMD Quad оказывается более чем вдвое быстрее, тогда как на «горячем» кэше выигрыш более скромный.

Но важней вывод заключается в том, что во всех случаях SIMD Quad быстрее бинарного поиска.

SIMD-компонент алгоритма довольно прост: мы используем специализированные инструкции, которые сокращают объем работы. ПОэтому легко понять, почму это может ускорить процесс - меньше инструкций, меньше ветвлений.

Но как насчет «четверичной» (quad) части? Тогда я опробовал бинарную версию того же алгоритма. В нем присутствует та же SIMD-оптимизация, но я отказался от четверичного интерполяционного поиска и заменяю его стандартным бинарным поиском.

Intel/GCC

Intel/GCC

Apple/LLVM

Apple/LLVM

Говоря простыми словами, подход с четверичным поиском (quad) почти и не дает эффекта на Apple, однако на Intel он является неплохой оптимизацией для больших массивов в «холодном» режиме. Четверичный поиск лучше задействует параллелизм на уровне памяти на моём Intel-сервере.

Мой исходный код доступен.


Заключение: мои результаты говорят о том, что, хотя классический бинарный алгоритм из учебника - вполне достойный, его можно улучшить, причем ощутимо. Стандартные алгоритмы зачастую не проектировались под компьютеры с таким объемом параллелизма. Алгоритм SIMD Quad пытается задействовать и параллелизм на уровне памяти, и параллелизм на уровне данных. Более того, я подозреваю, что можно добиться даже большего, чем даёт мой алгоритм. Будьте креативнее!

❯ Исходный код

bool simd_quad(const uint16_t *carr, int32_t cardinality, 
            uint16_t pos) {
    constexpr int32_t gap = 16;
    if (cardinality < gap) {
      for (int32_t j = 0; j < cardinality; j++) {
          if (carr[j] == pos) return true;
        }
        return false;
    }
    int32_t num_blocks = cardinality / gap;
    int32_t base = 0;
    int32_t n = num_blocks;
    while (n > 3) {
      int32_t quarter = n >> 2;

      int32_t k1 = carr[(base + quarter + 1) * gap - 1];
      int32_t k2 = carr[(base + 2 * quarter + 1) * gap - 1];
      int32_t k3 = carr[(base + 3 * quarter + 1) * gap - 1];

      int32_t c1 = (k1 < pos);
      int32_t c2 = (k2 < pos);
      int32_t c3 = (k3 < pos);

      base += (c1 + c2 + c3) * quarter;
      n -= 3 * quarter;
    }
    while (n > 1) {
        int32_t half = n >> 1;
        base = (carr[(base + half + 1) * gap - 1] < pos) 
                 ? base + half : base;
        n -= half;
    }
    int32_t lo = (carr[(base + 1) * gap - 1] < pos) 
                ? base + 1 : base;

    if (lo < num_blocks) {
        const uint16_t *blk = carr + lo * gap;
#ifdef __ARM_NEON
        uint16x8_t needle = vdupq_n_u16(pos);
        uint16x8_t v0 = vld1q_u16(blk);
        uint16x8_t v1 = vld1q_u16(blk + 8);
        uint16x8_t hit = vorrq_u16(vceqq_u16(v0, needle), 
                  vceqq_u16(v1, needle));
        return vmaxvq_u16(hit) != 0;
#else
        __m128i needle = _mm_set1_epi16((short)pos);
        __m128i v0 = _mm_loadu_si128((const __m128i *)blk);
        __m128i v1 = _mm_loadu_si128((const __m128i *)(blk + 8));
        __m128i hit = _mm_or_si128(_mm_cmpeq_epi16(v0, needle),
                                   _mm_cmpeq_epi16(v1, needle));
        return _mm_movemask_epi8(hit) != 0;
#endif
    }

    for (int32_t j = num_blocks * gap; j < cardinality; j++) {
        uint16_t v = carr[j];
        if (v >= pos) return (v == pos);
    }
    return false;
}
Может быть интересно:
Перейти ↩

Перейти

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале