#c #c #performance #bit-manipulation #gmp
Вопрос:
Существует mpz_class
оболочка C типа GMP mpz_t
. Имея mpz_class
число, каков наиболее эффективный способ использовать его N
младшие биты для создания другого mpz_class
числа?
Конечно, я могу выполнить следующую операцию маскировки
size_t N = 273; // how many lo bits to take
mpz_class x = ... ; // fill with something...
mpz_class mask = (mpz_class(1) << N) - 1; // mask having N 1-bits
mpz_class result = x amp; mask; // final result, N lowest bits taken
Но эта маскировка требует большого количества ненужных битовых операций и замедляет код. Может быть, для этого есть какой-нибудь короткий путь, например result = x.take_lo(N);
?
Также возможно, что mpz_class
отсутствует такой ярлык, но, по крайней мере, может быть, C API имеет эту функцию? Потому что любой mpz_class
из них может быть легко преобразован без дополнительных затрат в C-тип mpz_t
mpz_t c_num = x.get_mpz_t();
. Так что для меня нормально иметь .take_lo(N)
ярлык только в C API.
Комментарии:
1. «и замедляет код» Было бы неплохо, если бы вы отредактировали свой ответ, чтобы показать нам профилировщик, показывающий, что он действительно работает и является узким местом в вашем коде.
2. API C имеет
mpz_tdiv_r_2exp
: разделите на степень 2 и возьмите остаток. Это то, что вы хотите, и в нем используется очевидная эффективная реализация . Кажется, стоит попробовать.3. @Kaihaku Мой код интенсивно использует эти операции маскировки. Но в любом случае, когда вы занимаетесь кодированием, очень приятно знать все лучшие доступные функции/операции и использовать их. Здесь определенно делать И операции не нужно, вам просто нужно скопировать наименьшие N битов в новое число, и все, нет необходимости в И.
4. @NateEldridge Спасибо! Именно то, что нужно!
Ответ №1:
Хотя для оператора C нет перегрузки или функции mpz_class
, вы действительно можете использовать: mpz_tdiv_r_2exp
предоставлено C API. например,
mpz_tdiv_r_2exp(result.get_mpz_t(), x.get_mpz_t(), N);
примечание: cdiv
и fdiv
варианты также доступны.
Использование mp_bitcnt_t
в качестве типа для (N)
или static_cast<mp_bitcnt_t>(N)
в качестве аргумента было бы более надежным — как mp_bitcnt_t
, по-видимому, безоговорочно определено как unsigned long
, что может не совпадать size_t
.
Комментарии:
1. Вы уверены, что предложенная вами функция быстрее и оптимизирована, чем мое решение? Потому что я открыл вашу связанную документацию и в разделе функция увидел комментарий
For the 2exp variants the divisor is 2^b. These functions are implemented as right shifts and bit masks.
, так как я понимаю, что он выполняет маскировку битов, следовательно, делает то же самое, что и я, много ненужных И операций.2. Хорошо, я написал код для измерения времени, и ваша функция появилась
2.1x
в разы быстрее, чем мое решение для маскировки И маскировки. Так что, если ваша функция все еще работает И работает, то, по крайней мере, она делает это в два раза быстрее. Так что принимаю и одобряю ваш ответ. Спасибо!3. Как @NateEldredge опубликовал ссылку на код в комментарии, по этой ссылке можно увидеть, что
mpz_tdiv_r_2exp
на самом деле это не так, и просто копирует более низкие данные числа. Так что это вполне оптимально.