С недавних пор помешался на асме и оптимизации Взялся оптимизировать свой ацкий алгоритм начала прошлого года. Алгоритм много раз вызывал функцию нахождения ближайшей к произвольному числу степени 2. А в ней было использовано последовательное возведение в степень и инкремент, если меньше Ну тык вот, дооптимизировался я до нижеприведённого кода (вход/выход через EAX). В связи с чем вопрос: ведь вот чувствую, что сие можно ещё улучшить, только не пойму, как. Помогёте? Код (Text): MOV EDX, 1; BSR ECX, EAX; JE @exit; SHL EDX, CL; SUB EAX, EDX; ADD EAX, EAX; CMP EAX, EDX; MOV EAX, EDX; JLE @exit; <---- JL, если до большего (как в случае EAX=6) ADD EAX, EAX; @exit:
Ближайщая степень двойки - это номер старшего бита в числе, как я понимаю. Код (Text): mov eax,[number] mov ebx,31 @@: bt eax,ebx jc @F dec ebx jmp @B @@: ;В ebx - степень А до меньшего ещё надо dec ebx сделать. [del]
Ну, не совсем так. Есть у нас, скажем, число 190. Оно лежит в промежутке от 128 до 256. Нужно определить, к какому из двух "концов" оно ближе. В данном случае - к 128. Его и пишем. А если мы наткнёмся на 193, то оно тяготеет более к 256. Можете сами проверить. А 192 - это среднее арифметическое 128 и 256. Возникает особая ситуация, которая командой JLE трактуется в сторону 128, JL - в сторону 256 (см. коммент). А в коде из [1] берётся просто старший ненулевой бит числа.
Код (Text): mov eax, 6 bsr ecx, eax shr eax, cl adc ecx, 0 shl eax, cl но не думаю что лучший вариант
хм... По памяти улучшение налицо! И решение интересное (мне бы и в голову не пришло юзать здесь ADC)! Однако по скорости оптимизации, увы, нет: четыре данные операции (не считая загрузку числа в ЕАХ) выполняются в среднем за 890 мс на 10^8 проходов, а исходные 10 - за 520. Наверняка сказывается тормознутость ADC. Однако всё равно спасибо! ЗЫЖ Забыл сказать: код "затачивается" под Celeron 2400. Предлагайте ещё варианты =)
Смысл в том, чтобы воспользоваться bsr, а затем проверить следующий бит. bsr ecx,eax mov edx, [table+ecx*4] test eax, edx jz @f add ecx, 1 @@: mov eax,ecx table: dd 0 dd 1 dd 2 dd 4 ... Я не помню точно поведение bsr, а доков нет, поэтому table придётся поправить
ага, а еще про bt Код (Text): bsr ecx, eax add eax, eax bt eax, ecx mov eax, 0 adc ecx, 0 bts eax, ecx но опять же, одна команда от другой зависит
На основании нескольких вышеприведённых сырцов (респект за идею) было написано это: Код (Text): BSR ECX, EAX; SHR EAX, CL; JNC @skip; ADD ECX, EAX; <---- EAX = 1 @skip: SUB EAX, 1; BTS EAX, ECX; И - о чудо! - минус 100 миллисекунд! 420 вместо 520! Однако вся проблема в том, что в "спорных" случаях этот (и вышеуказанные) алгос приводит число к большему! А нужно - к меньшему =( Видимо, всё-таки придётся пользовать таблицы...
Код (Text): bsr ecx,eax mov edx, [table+ecx*4] test eax, edx jz @f add ecx, 1 @@: mov eax,ecx table: dd 0 dd 1 dd 2 dd 4 ... угу. ИМХО составление таблицы средних арифметических 0x01, 0x02, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, ... и сравнение с eax побыстрее будет. Код (Text): bsr ebx, eax xor ecx, ecx cmp eax, [table + ebx*4] setg(e) cl add ebx, ecx результат в ebx.
Нам нравится вариант от KeSqueer Только если в исходном регистре ноль, то bsr даёт undefined результат в destination. Так что так (вход, выход - eax): Код (Text): xor edx, edx bsr ecx, eax setz dl sub dl, 1 add eax, eax bt eax, ecx adc ecx, 0 and ecx, edx mov eax, ecx А что, правда bt настолько тормозная, что выгоднее через таблицу делать?
Код (Text): dec eax bsr ecx, eax shr eax, cl adc ecx, 0 ;and ecx, 0x1F либо второй вариант, тоже с dec eax вначале
Тогда лучше без adc и btX. Что-то типа Код (Text): bsr ecx,eax jz @F mov edx,-1 mov ebx,1 shl edx,cl sub ecx,1 jle @F shl ebx,cl lea eax,[eax+ebx-1] and eax,edx @@: