- PVSM.RU - https://www.pvsm.ru -
Это псевдорасшифровка моей презентации на !!Con 2019 [1].
В большинстве используемых сегодня процессорных архитектур есть инструкция под названием popcount
, сокращённо от 'population count'. Она делает следующее: подсчитывает количество установленных битов в машинном слове. Например (возьмём 8-битные слова для простоты), popcount(00100110)
равно 3, а popcount(01100000)
равно 2.
Вас это может сильно удивить, как и меня, но это всё, что она делает! Кажется не очень полезным, правда?
Я думал, что это какое-то недавнее дополнение для некоторых гиперспециализированных вариантов использования, но на самом деле она присутствует в архитектурах процессоров, по крайней мере, с 1961 года:
Так что же происходит?
popcount
также известна как «инструкция АНБ», и в очень интересном треде на comp.arch [9] обсуждается её использование в криптографии. Ходят слухи, что её первоначально добавили в набор инструкций CPU по просьбе АНБ. Как сказано в этом архивированном почтовом треде [10]:
Было почти традицией одну из каждой партии более быстрых машин CDC отправлять «хорошему клиенту» — приезжал неизвестный грузовик, и больше о ней никогда не слышали.
Отличная легенда, но для чего они её использовали?
Один из показателей информационного содержания — вес Хэмминга [11], который представляет собой количество ненулевых символов в строке. Для двоичной строки это именно popcount
!
Как объяснялось здесь [12], АНБ требовалось проводить криптоанализ перехваченных сообщений, а поскольку CDC 6000 работала с 60-битными словами, одного слова было достаточно для хранения большинства алфавитов, которые их интересовали. Они были в состоянии:
popcount
для подсчёта числа разных символов
Любопытно, что popcount
, похоже, исчез из наборов инструкций между серединой 1970-х и серединой 2000-х годов, поэтому возвращение должно объясняться чем-то ещё, кроме криптографических приложений. Для чего ещё её можно использовать?
С понятием веса Хэмминга связано расстояние Хэмминга [13], которое представляет собой количество различных позиций между двумя строками одинаковой длины. Для двух двоичных строк x
и y
, это просто popcount
после XOR. Например:
00100110 01100000 ^ -------- 01000110 popcount(01000110) = 3
В телекоммуникационных приложениях это помогает вычислить расстояние сигнала, где по проводу передаётся известное слово и подсчитывается количество изменённых битов, чтобы оценить ошибку передачи.
Затем мы можем спроектировать соответствующий код исправления ошибок [14]. Например, если передача должна выдерживать до двух изменённых битов, то кодовые слова должны отличаться по расстоянию Хэмминга по крайней мере на 5.
А теперь нечто совсем иное: двоичные свёрточные нейронные сети! Но сначала, что это такое?
Таким образом, мы должны выполнить умножение бинарных матриц. Но что особенного в бинарных матрицах?
Обычное умножение матриц на 32-битные значения хорошо подходит для настольных компьютеров с мощными CPU и GPU, но всё чаще мы хотим выполнять полезную работу на небольших и простых устройствах, таких как смартфоны, маршрутизаторы, умные часы и т. д. Мы можем разложить эти более сложные матрицы на слои бинарных матриц, а с ними настолько легче работать и хранить их, что мы в выигрыше даже несмотря на увеличение количества слоёв.
Тут вступает в игру popcount
. Он используется для вычисления скалярного произведения двух бинарных матриц:
a = xnor(x, y) b = popcount(a) c = len(a) dot(x, y) = 2 × b − c
Более подробно см. здесь [16] и здесь [17].
Многие шахматные программы хранят данные в представлении bitboard [18], которое удобно вписывается в 64-битное слово. Операция Population Count
использовалась для значимых операций с этим представлением, таких как вычисление подвижности [19] фигуры.
Это тоже связано с расстоянием Хэмминга: молекулы каким-то образом хэшируются и сравниваются (с помощью popcount
), чтобы определить степень их похожести. Подробнее см. здесь [20].
Вот где я впервые узнал о popcount
! HAMT — это структура данных (впервые созданная Филом Бэгвеллом [21]), которая может хранить очень большое количество значений (обычно 32 или 64) в массиве на каждом узле trie. Однако выделение памяти для 32 или 64-элементного массива каждый раз может быть невероятно расточительным, особенно если массив на самом деле содержит всего несколько элементов. Решение состоит в том, чтобы добавить битовую маску, в которой количество установленных бит соответствует количеству элементов в массиве, что позволяет массиву расти и сжиматься по мере необходимости. Вычисление индекса для данного элемента эффективно может быть сделано с помощью popcount
. В моём блог-посте [22] с реализацией структур HAMT можете узнать больше, как они работают.
Это захватывающая новая область исследований, которая фокусируется на том, как хранить данные в минимальном пространстве, не распаковывая их для выполнения полезной работы. Один из методов состоит в том, чтобы думать в терминах массивов битов (бит-векторы), которые можно запросить двумя операциями:
rank(i)
подсчитывает количество бит, заданных до i-го индекса в бит-векторе
select(i)
находит индекс, в котором установлен i-й бит
Чтобы сделать эти операции эффективными на больших бит-векторах, требуется построить индекс и эффективно его использовать, в обоих случаях с участием popcount
. Вот здесь [23] есть хороший обзор индекса RRR. И, насколько я могу судить, самый передовой современный подход описан в статье Space-Efficient, High-Performance Rank & Select Structures on Uncompressed Bit Sequences [24].
popcount
стала настолько распространённой, что и GCC [25], и Clang [26] способны её обнаружить и заменить встроенной инструкцией. Представьте такого Клиппи: «О, я вижу, что вы пытаетесь реализовать popcount
, позвольте мне выйти и исправить это для вас»! Соответствующий код LLVM находится здесь [27]. Даниэль Лемир приводит его как пример удивительного ума современных компиляторов.
Окутанная тайной в начале своей истории, инструкция popcount
стала повсеместно использоваться, хотя и осталась немного необычной инструкцией CPU. Мне нравится, как она связывает такие разные области информатики, и мне интересно, сколько ещё существует других таких же странных инструкций. Если у вас есть своя любимая, хотелось бы услышать о ней!
Автор: m1rko
Источник [28]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/algoritmy/329752
Ссылки в тексте:
[1] моей презентации на !!Con 2019: https://www.youtube.com/watch?v=bLFqLfz2Fmc
[2] IBM Stretch: https://en.wikipedia.org/wiki/IBM_7030_Stretch
[3] CDC 6000: https://en.wikipedia.org/wiki/CDC_6000_series
[4] Cray-1: https://en.wikipedia.org/wiki/Cray-1
[5] SPARC: https://en.wikipedia.org/wiki/SPARC
[6] ARM NEON: https://en.wikipedia.org/wiki/ARM_architecture#Advanced_SIMD_(NEON)
[7] AMD K10: https://en.wikipedia.org/wiki/AMD_10h
[8] Intel Nehalem: https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
[9] очень интересном треде на comp.arch: https://groups.google.com/forum/#!msg/comp.arch/UXEi7G6WHuU/Z2z7fC7Xhr8J
[10] этом архивированном почтовом треде: http://cryptome.org/jya/sadd.htm
[11] вес Хэмминга: https://en.wikipedia.org/wiki/Hamming_weight
[12] объяснялось здесь: http://www.talkchess.com/forum3/viewtopic.php?t=38521
[13] расстояние Хэмминга: https://en.wikipedia.org/wiki/Hamming_distance
[14] код исправления ошибок: https://en.wikipedia.org/wiki/Hamming_distance#Error_detection_and_error_correction
[15] мозгом: http://www.braintools.ru
[16] здесь: https://sushscience.wordpress.com/2017/10/01/understanding-binary-neural-networks/
[17] здесь: https://developer.apple.com/documentation/metalperformanceshaders/mpscnnbinaryconvolution
[18] bitboard: https://www.chessprogramming.org/Bitboards
[19] подвижности: https://www.chessprogramming.org/Mobility#Mobility_with_Bitboards
[20] здесь: http://www.dalkescientific.com/writings/diary/archive/2008/06/26/fingerprint_background.html
[21] впервые созданная Филом Бэгвеллом: https://lampwww.epfl.ch/papers/idealhashtrees.pdf
[22] моём блог-посте: https://vaibhavsagar.com/blog/2018/07/29/hamts-from-scratch/
[23] здесь: https://alexbowe.com/rrr/
[24] Space-Efficient, High-Performance Rank & Select Structures on Uncompressed Bit Sequences: http://www.cs.cmu.edu/~./dga/papers/zhou-sea2013.pdf
[25] GCC: https://godbolt.org/z/JUzmD8
[26] Clang: https://godbolt.org/z/AVqMGl
[27] здесь: https://github.com/llvm-mirror/llvm/blob/f36485f7ac2a8d72ad0e0f2134c17fd365272285/lib/Transforms/Scalar/LoopIdiomRecognize.cpp#L960
[28] Источник: https://habr.com/ru/post/467083/?utm_campaign=467083&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.