Самый быстрый способ получить мод 10 в C - PullRequest
0 голосов
/ 26 апреля 2020

В моей программе прекрасно присутствует операция n % 10. Я знаю, что работа модуля может быть выполнена намного быстрее, когда у нас есть n% m, где m - степень 2, поскольку его можно заменить на n & (m-1 ), однако есть ли более быстрый способ вычисления модуля, если операнд равен 10? В моем случае n - это uint8_t, а в некоторых случаях n - это uint32_t.

Ответы [ 2 ]

0 голосов
/ 26 апреля 2020

Поскольку большинство современных процессоров могут выполнять умножение намного, намного быстрее, чем деление, часто можно ускорить операции деления и модуля, где дивиденд является известной малой константой, заменяя деление одним или двумя умножениями и несколькими другими быстрыми операции (такие как сдвиг и сложение).

Для этого требуется вычислить во время компиляции некоторые числа волхвов c, зависящие от дивиденда; К счастью, большинство современных компиляторов знают, как это сделать, поэтому вам не нужно ничего делать, чтобы воспользоваться этим. Просто позвольте вашему компилятору сделать тяжелую работу за вас, как @ chux предлагает в отличный ответ .

Вы можете помочь компилятору, используя неподписанные типы; для некоторых дивидендов сложнее заменить подписанное деление и модуль.

Базовый c план оптимизации модуля выглядит следующим образом:

Если у вас была точная арифметика c, вы может заменить x % p на p * ((x * (1/p)) % 1). Для константы p, 1/p может быть предварительно вычислено во время компиляции. Операция %1 просто состоит в отбрасывании дробной части, которая представляет собой просто сдвиг вправо. Таким образом, это заменяет деление на два умножения, и если p имеет только несколько установленных битов, умножение на p может быть дополнительно оптимизировано в несколько сдвигов влево.

Мы можем сделать это вычисление с арифметика с фиксированной точкой c, использующая преимущество того факта, что большинство процессоров выдают результат двойного размера для целочисленного умножения. Поскольку нам не важна целочисленная часть внутреннего умножения, и мы знаем, что результат внешнего умножения должен быть меньше p, нам нужно только зарезервировать биты ceil (log2 p) для целочисленной части вычисления. оставляя остальные биты для дроби. И это может дать нам достаточную точность для правильной обработки возможного диапазона значений x, особенно если x имеет ограниченный диапазон (например, uint8_t или даже uint16_t). Ключ находит позицию фиксированной точки, которая минимизирует ошибку в представлении 1/p.

Для многих небольших значений p это работает. Для других есть альтернативное (но более медленное) решение, которое включает в себя оценку q = x/p с использованием умножения на обратное, а затем вычисление x - q * p. Если оценка q может быть гарантирована как правильная или отклоненная на единицу в известном направлении, нам нужно только исправить окончательное вычисление путем условного прибавления или вычитания p; это может быть достигнуто без ветки на многих современных процессорах. (Направление ошибки известно, поскольку оно будет зависеть только от того, было ли выбранное нами приближение для обратного делимого слишком маленьким или слишком большим.)


В очень конкретном случае c из x % 10, где x - это uint_8, вы можете добиться большего успеха, чем выше, используя 256-байтовую таблицу поиска. Это было бы целесообразно, если бы вы выполняли операцию модуля в узком l oop для большого числа значений, и даже тогда вы захотите тщательно профилировать, чтобы убедиться, что это улучшение.

Я сомневаюсь, что это лучшая трата вашего времени; возможно, в вашем приложении гораздо более полезные возможности оптимизации.

0 голосов
/ 26 апреля 2020

однако Есть ли более быстрый способ вычисления модуля, если операнд равен 10?

При хорошем компиляторе нет. Компилятор уже выпустил бы хороший код. Вы можете изучить различные параметры оптимизации с помощью компилятора.

OTOH, если вы знаете о некоторых ограничениях, которые компилятор не может принять с n % 10, например, значения всегда положительны или имеют поддиапазон, вы можете оптимизировать компилятор.

Такая микрооптимизация обычно неэффективно использует время программиста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...