HackMaster & Drunik
Drunik Drunik
Я тут как раз небольшую рутину для radix sort тестировал, и понял, что размер строки кэша L1 – вот где настоящая проблема. Пробовал когда-нибудь полностью векторизованную версию, которая ещё и выравнивает корзины по кэшу?
HackMaster HackMaster
Кажется, прямо кошмар с промахами кэша. Я бы задействовал SIMD-путь и выравнивал каждый бакет на полную линию, чтобы железо могло просто пробегаться по нему. Главное – следить за тем, чтобы указатели на бакеты были выровнены на всех границах. Попробовал – пропускная способность взлетела, кэш был горячим, конвейер работал как часы. Нужен повторный просмотр трюков с битовыми операциями?
Drunik Drunik
Конечно. Чтобы получить следующее число, кратное степени двойки, для 32-битного слова, можно сделать так: вычитаешь единицу, потом выполняешь операции "или" со сдвигами — x |= x >> 1, x |= x >> 2, x |= x >> 4, x |= x >> 8, x |= x >> 16, и затем прибавляешь единицу. Это "зажимает" любое число, не являющееся степенью двойки, округляя его до следующей большей степени. Для сканирования битов используй инструкцию bsf процессора, если пишешь на ассемблере; на C можно использовать __builtin_ctz, чтобы найти индекс младшего установленного бита. Если нужен индекс старшего — используй __builtin_clz. А для обнуления младшего установленного бита делай x &= x - 1. Эти приемы позволяют тебе оставаться в области целочисленных операций, без сюрпризов с плавающей точкой, и они работают быстро на современных конвейерах.
HackMaster HackMaster
Отлично, это стандартный рецепт. Просто убедись, что используешь правильный тип данных, иначе встроенные операции могут привести к неверному значению при расширении знака, и ты получишь неправильный подсчет для 32-битного беззнакового числа. Если это в критической внутренней петле, подумай о предварительном вычислении степеней двойки для размеров корзин, чтобы не тратить время на сдвиги каждый раз. Что-нибудь еще пытаешься выжать из этапа обработки основания?
Drunik Drunik
Просто подумал: если у тебя основание системы — 256, количество корзин всегда будет степенью двойки, и тогда вместо операции взятия остатка можно использовать маску. Это сэкономит одну инструкцию деления и позволит компилятору держать индекс в регистре. И еще, если расположить корзины в массиве непрерывно и выровнять по 64 байта, избежишь разбиения строк кэша при записи. И не забудь использовать ключевое слово "restrict" для указателей на источник и назначение – компилятор без проблем вынесет цикл из вложенности, если увидит, что алиасинг невозможен.
HackMaster HackMaster
Звучит напряженно – маскировка – верный путь, и подсказка для restrict – отличная победа для оптимизатора. Если ты все еще копаешься с границами, попробуй обнулить указатели корзин; это очистит путь записи и позволит конвейеру не рассортиться. Удачи в погоне за этими последними циклами.
Drunik Drunik
Проверь, пожалуйста, чтобы ты случайно не обнулил защитные записи; последний контейнер немного сложнее. Удачи в поисках.
HackMaster HackMaster
Понял, буду прикрывать стража, чтобы финальный сбор остался в живых. Удачи в охоте.