Существует ли простой путь к извлечению экспоненты от питания 2 битовых операций использования только?
Править: Хотя вопрос был первоначально о битовых операциях, поток является хорошим чтением также, если Вы задаетесь вопросом, "Что самый быстрый путь состоит в том, чтобы найти X данными Y = 2X в Python?" **
Я в настоящее время пытаюсь оптимизировать стандартную программу (тест простоты чисел Rabin-Miller), который сокращает четное количество N в формах 2**s * d
. Я могу добраться 2**s
часть:
two_power_s = N & -N
но я не могу найти способ извлечь просто "s" с битовой операцией. Обходные решения, которые я в настоящее время тестирую без слишком большой удовлетворенности (они являются все в значительной степени медленными):
Я использую Python, но ответ на этот вопрос должен быть агностиком языка, я предполагаю.
Что касается python:
timeit.Timer.repeat(testn, cycles)
где testn
был установлен на 3, а cycles
был автоматически скорректирован скриптом для получения времени в диапазоне секунд (примечание: в этом механизме автокоррекции была ошибка, которая была исправлена 18/02/2010). func(25)**
hashlookup: 0.13s 100%
lookup: 0.15s 109%
stringcount: 0.29s 220%
unrolled_bitwise: 0.36s 272%
log_e: 0.60s 450%
bitcounter: 0.64s 479%
log_2: 0.69s 515%
ilog: 0.81s 609%
bitwise: 1.10s 821%
olgn: 1.42s 1065%
func(231)**
hashlookup: 0.11s 100%
unrolled_bitwise: 0.26s 229%
log_e: 0.30s 268%
stringcount: 0.30s 270%
log_2: 0.34s 301%
ilog: 0.41s 363%
bitwise: 0.87s 778%
olgn: 1.02s 912%
bitcounter: 1.42s 1264%
func(2128)**
hashlookup: 0.01s 100%
stringcount: 0.03s 264%
log_e: 0.04s 315%
log_2: 0.04s 383%
olgn: 0.18s 1585%
bitcounter: 1.41s 12393%
func(21024)**
log_e: 0.00s 100%
log_2: 0.01s 118%
stringcount: 0.02s 354%
olgn: 0.03s 707%
bitcounter: 1.73s 37695%
import math, sys
def stringcount(v):
"""mac"""
return len(bin(v)) - 3
def log_2(v):
"""mac"""
return int(round(math.log(v, 2), 0)) # 2**101 generates 100.999999999
def log_e(v):
"""bp on mac"""
return int(round(math.log(v)/0.69314718055994529, 0)) # 0.69 == log(2)
def bitcounter(v):
"""John Y on mac"""
r = 0
while v > 1 :
v >>= 1
r += 1
return r
def olgn(n) :
"""outis"""
if n < 1:
return -1
low = 0
high = sys.getsizeof(n)*8 # not the best upper-bound guesstimate, but...
while True:
mid = (low+high)//2
i = n >> mid
if i == 1:
return mid
if i == 0:
high = mid-1
else:
low = mid+1
def hashlookup(v):
"""mac on brone -- limit: v < 2**131"""
# def prepareTable(max_log2=130) :
# hash_table = {}
# for p in range(1, max_log2) :
# hash_table[2**p] = p
# return hash_table
global hash_table
return hash_table[v]
def lookup(v):
"""brone -- limit: v < 2**11"""
# def prepareTable(max_log2=10) :
# log2s_table=[0]*((1<<max_log2)+1)
# for i in range(max_log2+1):
# log2s_table[1<<i]=i
# return tuple(log2s_table)
global log2s_table
return log2s_table[v]
def bitwise(v):
"""Mark Byers -- limit: v < 2**32"""
b = (0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000)
S = (1, 2, 4, 8, 16)
r = 0
for i in range(4, -1, -1) :
if (v & b[i]) :
v >>= S[i];
r |= S[i];
return r
def unrolled_bitwise(v):
"""x4u on Mark Byers -- limit: v < 2**33"""
r = 0;
if v > 0xffff :
v >>= 16
r = 16;
if v > 0x00ff :
v >>= 8
r += 8;
if v > 0x000f :
v >>= 4
r += 4;
if v > 0x0003 :
v >>= 2
r += 2;
return r + (v >> 1)
def ilog(v):
"""Gregory Maxwell - (Original code: B. Terriberry) -- limit: v < 2**32"""
ret = 1
m = (not not v & 0xFFFF0000) << 4;
v >>= m;
ret |= m;
m = (not not v & 0xFF00) << 3;
v >>= m;
ret |= m;
m = (not not v & 0xF0) << 2;
v >>= m;
ret |= m;
m = (not not v & 0xC) << 1;
v >>= m;
ret |= m;
ret += (not not v & 0x2);
return ret - 1;
# following table is equal to "return hashlookup.prepareTable()"
hash_table = {...} # numbers have been cut out to avoid cluttering the post
# following table is equal to "return lookup.prepareTable()" - cached for speed
log2s_table = (...) # numbers have been cut out to avoid cluttering the post
На самом деле это комментарий к тесту производительности, опубликованному Mac. Я отправляю это как ответ, чтобы иметь правильное форматирование кода и отступы
mac, не могли бы вы попробовать развернутую реализацию bitseach, предложенную Марком Байерсом? Может быть, просто доступ к массиву тормозит. Теоретически этот подход должен быть быстрее, чем другие.
Это будет выглядеть примерно так, хотя я не уверен, подходит ли форматирование для python, но я думаю, вы можете видеть, что он должен делать.
def bitwise(v):
r = 0;
if( v > 0xffff ) : v >>= 16; r = 16;
if( v > 0x00ff ) : v >>= 8; r += 8;
if( v > 0x000f ) : v >>= 4; r += 4;
if( v > 0x0003 ) : v >>= 2; r += 2;
return r + ( v >> 1 );
Если python разделяет отсутствие в Java неотмеченных целых чисел, это должно быть примерно так:
def bitwise(v):
r = 0;
if( v & 0xffff0000 ) : v >>>= 16; r = 16;
if( v > 0x00ff ) : v >>= 8; r += 8;
if( v > 0x000f ) : v >>= 4; r += 4;
if( v > 0x0003 ) : v >>= 2; r += 2;
return r + ( v >> 1 );
«языковой агностик» и беспокойство о производительности - в значительной степени несовместимые понятия .
В большинстве современных процессоров есть инструкция CLZ «считать ведущие нули». В GCC вы можете добраться до него с помощью __builtin_clz (x) (который также дает разумный, если не самый быстрый, код для целей, в которых отсутствует clz). Обратите внимание, что этот CLZ не определен для нуля, поэтому вам понадобится дополнительная ветка, чтобы поймать этот случай, если он имеет значение в вашем приложении.
В CELT ( http://celt-codec.org ) внеофисный CLZ, который мы используем для компиляторов без CLZ, был написан Тимоти Б. Террибери:
int ilog(uint32 _v){
int ret;
int m;
ret=!!_v;
m=!!(_v&0xFFFF0000)<<4;
_v>>=m;
ret|=m;
m=!!(_v&0xFF00)<<3;
_v>>=m;
ret|=m;
m=!!(_v&0xF0)<<2;
_v>>=m;
ret|=m;
m=!!(_v&0xC)<<1;
_v>>=m;
ret|=m;
ret+=!!(_v&0x2);
return ret;
}
(комментарии указывают, что это было оказывается быстрее, чем версия с ветвлением и версия на основе таблицы поиска)
Но если производительность настолько критична, вам, вероятно, не следует реализовывать эту часть вашего кода на python.
Есть страница с большим количеством подобных трюков и хаков. Она написана для языка C, но многие из них должны работать и в Python (хотя производительность, очевидно, будет отличаться). Нужный вам фрагмент находится здесь и далее.
Вы можете попробовать вот это, например:
register unsigned int r = 0; // result of log2(v) will go here
for (i = 4; i >= 0; i--) // unroll for speed...
{
if (v & b[i])
{
v >>= S[i];
r |= S[i];
}
}
Похоже, что это можно легко преобразовать в Python.
У меня также есть индекс поиска lucene, который используется несколькими клиентами, я решаю эту проблему, делая «Службу поиска Lucene» отдельной веб-службой, работающей в собственном домене приложений. Поскольку оба клиента попали в одну веб-службу для поиска или обновления индекса, я могу сделать его защищенным от потоков с блокировками на индексаторах Lucene.
Кроме того, если вы хотите сохранить его в процессе, я предлагаю использовать блокировки файлов, чтобы убедиться, что только один клиент может записать в индекс.
Чтобы получить возможность использовать новый индекс, я создаю его на стороне, а затем предлагаю службе Search Index поменяться местами для использования нового индекса путем безопасного удаления любых индексаторов в текущем индексе и переименования каталогов, Например,
Я думаю, что необходимо включить проверку выполнения контракта в параметрах настройки проекта (должна быть панель «Code Contracts»...)
Для получения дополнительной информации см. пользовательскую документацию (раздел 6).
-121--4378655-Похоже, диапазон известен. Давайте предположим, что он поднимается до 1 < < 20, просто чтобы сделать его более интересным:
max_log2=20
Поэтому составьте список, который (по сути) отображает целое число на его основание 2 логарифм. Хитрость сделают следующие:
log2s_table=[0]*((1<<max_log2)+1)
for i in range(max_log2+1):
log2s_table[1<<i]=i
(Это не делает ничего полезного для чисел, которые не являются степенями двух; Постановки задачи предполагает, что с ними не нужно справляться. Было бы достаточно легко исправить это, хотя.)
Функция для получения логарифма очень проста и может быть легко встроена:
def table(v):
return log2s_table[v]
Я не могу гарантировать, что код теста, который я написал, точно такой же, как тот, который используется для получения примеров таймингов, но это скорее быстрее, чем код stringcount
:
stringcount: 0.43 s.
table: 0.16 s.
Поскольку все значения в таблице меньше 256, я задался вопросом, будет ли использование последовательности вместо списка быстрее, или, может быть, array.array
байтов, но без кубиков:
string: 0.25 s.
arr: 0.21 s.
Использование dict
для выполнения поиска является еще одной возможностью, используя путь проверки только двух полномочий:
log2s_map=dict([(1<<x,x) for x in range(max_log2+1)])
def map(v):
return log2s_map[v]
Результаты для этого были не так хороши, хотя:
map: 0.20 s.
И просто для удовольствия можно было бы также использовать шестнадцатеричный
метод на плавающих объектах, чтобы получить последовательность, который включает (в качестве его последней части) базовый 2 экспонента числа. Это немного медленно извлекается в целом, но если экспонента когда-либо будет только одна цифра, это может быть сделано достаточно просто:
def floathex(v):
return ord(float(v).hex()[-1])-48
Это чисто для развлекательной ценности, хотя, как это было неконкурентоспособно - хотя, удивительно, все еще быстрее, чем побитовый подход.
Похоже, что использовать список - это путь.
(Этот подход не будет масштабироваться бесконечно из-за ограниченной памяти, но для того, чтобы компенсировать, что скорость выполнения не будет зависеть от max _ log2
или входных значений, в любом пути, которое вы заметите при запуске кода python. Что касается потребления памяти, то, если я правильно запомню внутренние устройства питона, таблица займет около (1 < < max _ log2) * 4
байт,потому что содержимое все небольшие целые числа, которые интерпретатор будет интернировать автоматически. Итак, когда max _ log2
равно 20, это примерно 4MB.)
Вы можете сделать это за время O(lg s) для целых чисел произвольной длины, используя binsearch.
import sys
def floorlg(n):
if n < 1:
return -1
low=0
high=sys.getsizeof(n)*8 # not the best upper-bound guesstimate, but...
while True:
mid = (low+high)//2
i = n >> mid
if i == 1:
return mid
if i == 0:
high = mid-1
else:
low = mid+1
Для целых чисел фиксированного размера, таблица поиска должна быть самым быстрым решением, и, вероятно, лучшим в целом.