Быстрое получение двоичного порядка числа типа Long.

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Быстрое получение двоичного порядка числа типа Long.

Сообщение Mikle » 15.04.2008 (Вт) 9:01

Встала задача получить Subj, причем максимально быстро, находится в очень узком месте программы.
То есть для 0 ф-ция должна возвращать 0,
для 1 - 1
для 2, 3 - 2
для 4..7 - 3
...
для отрицательных - 32

Первое решение "в лоб":
Код: Выделить всё
Const iLog2 As Double = 1.44269504089041

Public Function L2log(ByVal v As Long) As Long
  If v = 0 Then
    L2log = 0
  ElseIf v < 0 Then
    L2log = 32
  Else
    L2log = Int(Log(v) * iLog2) + 1
  End If
End Function


Не устроило быстродействие, стал искать другие варианты. Написал DLL на асме, код в PB:
Код: Выделить всё
FUNCTION ILOG2(BYVAL SRC AS LONG) EXPORT AS LONG
  ! bsr eax, src
  ! jnz  lab1
  ! xor eax, eax
  ! mov FUNCTION, eax
Exit Function
lab1:
  ! inc eax
  ! mov FUNCTION, eax

End Function

Ускорение более, чем в 2 раза. Уже "завелся", нашел третий вариант:
Код: Выделить всё
Dim bTab(32) As Long

Public Function L2Tab(ByVal v As Long) As Long
Dim n As Long
  n = 32
  While n > 0
    If v And bTab(n) Then
      L2Tab = n
      Exit Function
    End If
    n = n - 1
  Wend
End Function

Public Sub TabInit()
Dim n As Long
  For n = 0 To 32
    If n = 0 Then
      bTab(n) = 0
    ElseIf n < 32 Then
      bTab(n) = 2 ^ (n - 1)
    Else
      bTab(n) = &H80000000
    End If
  Next n
End Sub

Еще в 3 раза быстрее!

Может есть варианты еще?
Вложения
Exp2.rar
(7.73 Кб) Скачиваний: 124

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.04.2008 (Вт) 9:40

Логически лучше третьего варианта ничего не будет.
Единственное, я бы инициализацию степеней двойки оформил бы не отдельной процедурой, а как Error Handler, а в L2Tab написал бы On Error GoTo ErrorHandlerInitPowers.
Да и в инициализации 2^(n-1) не самое лучшее решение. В конце концов 32 значения можно хоть из ресурсов загрузить, или напрямую кодом задать. Либо логическими операциями обойтись.
Lasciate ogni speranza, voi ch'entrate.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Сообщение Mikle » 15.04.2008 (Вт) 12:47

alibek
2^(n-1) не самое лучшее решение

Однократное применение степени при старте программы ничего не решает.
Логически лучше третьего варианта ничего не будет.

Закрались-таки подозрения. Начнем с того, что она работает быстро благодаря несоответствию теста поставленной задаче :(
Сейчас в функцию поступают 50% чисел с порядком 32, 25% с порядком 31, 12.5% с порядком 30 и т. д. А в моей задаче порядок чисел примерно равновероятен. Соответственно переписываем тестирующий цикл так, код в форме:
Код: Выделить всё
Dim m(32) As Long

Private Sub Form_Load()
Dim n As Long
  QFreqInit
  TabInit
  For n = 0 To 32
    If n = 0 Then
      m(n) = 0
    ElseIf n = 32 Then
      m(n) = -Int(2 ^ (n - 2 + Rnd))
    Else
      m(n) = Int(2 ^ (n - 1 + Rnd))
    End If
  Next n
End Sub

Private Sub bTab_Click()
Dim n As Long, t As Double, D As Long, I As Long
  t = QTime
  For I = 0 To 999999
    For n = 0 To 32
      D = L2Tab(m(n))
    Next n
  Next I
  Caption = QTime - t
End Sub

И Tab уже отстал от Asm.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.04.2008 (Вт) 13:00

Ну если хочешь оптимизировать, то можно сравнивать степени не подряд, сверху вниз, а бинарным поиском. Это в 2-4 раза ускорит алгоритм.
Lasciate ogni speranza, voi ch'entrate.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Сообщение Mikle » 15.04.2008 (Вт) 13:27

alibek
бинарным поиском

Методом половинного деления? Мысль.
Но я нашел еще другой вариант, ускорение почти втрое!
Кто поймет, как работает - тому медаль. :D
Вложения
iLog2.rar
(8.86 Кб) Скачиваний: 121

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.04.2008 (Вт) 13:57

Это беспардонный хак :)
Заставляешь процессор за тебя степени вычислять для float? :)
Lasciate ogni speranza, voi ch'entrate.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 15.04.2008 (Вт) 23:27

Гонишься за производительностью и тормозишь на элементарном :)
1) Declare Function медленный. Фтопку Declare Function когда речь идёт о высокопроизодительном вызове высокопроивзодительных API.
2) PB генерирует пролог. Пролог совершенно никому не нужный, отнимающий лишние такты.

Я написал просто на асме следующую функцию:
Код: Выделить всё
        mov     edx, [esp+04]
        test    edx, edx
        js       ZeroProcessing
        bsr     eax, edx
        inc     eax
        retn    4 
        ZeroProcessing:
        xor     eax, eax
        retn    4

И я объявил её в TLB.
Этот вариант стал ещё в 2 раза быстрее (чем твой самый быстрый вариант).


И я написал свою функцию:
Код: Выделить всё
BitTest:
        mov     edx, [esp+04]
        mov     ecx, edx
        shr     edx, 16d
        xor     eax, eax
        test    edx, edx
        jz      loword_processing
        test    dh, dh
        jz      lobyte_processing
        shr     edx, 8
        or      al, byte[bit_table+edx]
        add     eax, 24
        retn    4
lobyte_processing:
        or      al, byte[bit_table+edx]
        add     eax, 16
        retn    4

loword_processing:
        or      dx, cx
        test    dh, dh
        jz      loword_lobyte_processing
        shr     edx, 8
        or      al, byte[bit_table+edx]
        add     eax, 8
        retn    4
loword_lobyte_processing:
        or      al, byte[bit_table+edx]
        retn    4


section '.data' data readable
db       0, 0, 0
bit_table:
db       0
db       1      ; 1
db       2, 2
db       3, 3, 3, 3
db       4, 4, 4, 4, 4, 4, 4, 4
db       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
db       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
db       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
db       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8


Но к сожалению, не обогнал bsr. Среднестатистическое время одинаковое.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Сообщение Mikle » 16.04.2008 (Ср) 8:58

Хакер
не обогнал bsr. Среднестатистическое время одинаковое

Тогда выбираем bsr, как меньший по размеру.
Я совершенно не умею работать с TLB :(
Не дашь сорцы?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 16.04.2008 (Ср) 9:39

Держи
Вложения
bittest.rar
Биттест с моим методом.
(11.62 Кб) Скачиваний: 129
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Сообщение Mikle » 17.04.2008 (Чт) 9:48

alibek
я бы инициализацию степеней двойки оформил бы не отдельной процедурой, а как Error Handler

Это заставляет избавиться от оптимизации Remove Array Bounds Checks. Скорость теста при этом падает на 16%, а вот скорость моей программы аж в три раза! Это совершенно неприемлемо.
Хакер
Благодарю.


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Google-бот, SemrushBot и гости: 10

    TopList