Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Общий форум

Страница: 1 | 2 | 3 | 4 |

 

  Вопрос: HScroll в ListBox Добавлено: 09.03.05 23:05  

Автор вопроса:  ZagZag | ICQ: 295002202 

Ответить

  Ответы Всего ответов: 54  

Номер ответа: 31
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #31
Добавлено: 17.03.05 10:43
Ну вот собственно, по совету crestа'ы переписал поиск на ассемблер. Вернее не весь, а критическую часть. Вот посмотрите может найдёте где можно оптимизировать.

Function Include_ScanEqu(ByVal pInclude As Long, ByVal IncLen As Long, ByVal pInclude2 As Long, ByVal IncLen2 As Long, ByVal pWordToSearch As Long, ByVal pWordToSearch_Len As Long)As Long
#Register None
 'STDOUT FuncName$
 Dim CmnSrch As Long, PartFlag As Long, SnglPartSrch As Long, EquIncPosPtr As Long
 EquIncPosPtr=VarPtr(EquIncPos(0))

 If IncLen=IncLen2 Then
    CmnSrch=IncLen
 ElseIf IncLen>IncLen2 Then
    CmnSrch=IncLen2: SnglPartSrch=IncLen-IncLen2: PartFlag=1
 ElseIf IncLen<IncLen2 Then
    CmnSrch=IncLen: SnglPartSrch=IncLen2-IncLen: PartFlag=2
 End If
 If CmnSrch<0 Then Exit Function

Dim Tmp1 As Long, TmpAsc2 As Asciiz*3, TmpAscPtr As Asciiz Ptr
 TmpAscPtr=VarPtr(TmpAsc2)
 ' HiWord(EDX)=Lcase$(Left$(WordToSearch,2))
 ! MOV EAX,TmpAscPtr
 ! MOV EDX,pWordToSearch
 ! MOV DX,Word PTR [EDX]
 ! MOV WORD PTR [EAX],DX
 ! PUSH EDX
 TmpAsc2=LCase$(TmpAsc2)
 ! POP EDX
 ! SHL EDX,16
 ' End HiWord(EDX)

 ' EDX = Left$(WordToSearch,2)
 ' ECX = Len(1/2 part of include)
 ' ESI = 1/2 part of include (Left)
 ' EDI = 1/2 part of include (Right)
 ' EBX = Ubound(EquIncPos)
 ' EAX = VarPtr(EquIncPos(0))

 ! MOV ECX,CmnSrch
 ! MOV ESI,pInclude
 ! MOV EDI,pInclude2
  ! MOV EAX,pWordToSearch
 ! MOV DX, WORD PTR [EAX]

 ! MOV EAX, EquIncPosPtr
 ! MOV EBX, EquIncPos_Counter

WhileLen:
 ! CMP DX, WORD PTR [ESI]
 ! JE WrdFound
 ! CMP DX, WORD PTR [EDI]
 ! JE WrdFound2
 ! ROR EDX,16
   ! CMP DX, WORD PTR [ESI]
   ! JE WrdFound_1
   ! CMP DX, WORD PTR [EDI]
   ! JE WrdFound2_1
 ! ROR EDX,16
ContLoop:
 ! DEC ECX
 ! JZ Finish
 ! INC ESI
 ! INC EDI
 ! JMP WhileLen

WrdFound:
 ! MOV DWORD PTR [EAX+EBX*4],ESI
 ! INC EBX
 ! JMP ContLoop
WrdFound_1:
 ! MOV DWORD PTR [EAX+EBX*4],ESI
 ! INC EBX
 ! ROR EDX,16
 ! JMP ContLoop

WrdFound2:
 ! MOV DWORD PTR [EAX+EBX*4],EDI
 ! INC EBX
 ! JMP ContLoop
WrdFound2_1:
 ! MOV DWORD PTR [EAX+EBX*4],EDI
 ! INC EBX
 ! ROR EDX,16
 ! JMP ContLoop

SnglSrch:

Finish:
 ! MOV EquIncPos_Counter,EBX
 'STDOUT "EquIncPos_Counter=" & str$(EquIncPos_Counter)
 If EquIncPos_Counter>0 Then
    EquIncPos_Counter=EquIncPos_Counter-1
    Include_ScanEqu_Finish pInclude, pWordToSearch, pWordToSearch_Len
 End If
End Function

Ответить

Номер ответа: 32
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #32
Добавлено: 17.03.05 10:54
 Вобщем компилятору такой код и в страшном сне не приснится :)
 Вот за что я люблю ассемблер, так за то что плевал я на все правила и стандарты.

Ответить

Номер ответа: 33
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #33 Добавлено: 17.03.05 14:35
Вот за что я люблю ассемблер, так за то что плевал я на все правила и стандарты

Это точно. Как говорят, "Что хочу, то и ворочу". И никто мне не указ, никаких ограничений.

Если я правильно понял, то это реализация INSTRING ? Комментариев ноль, поэтому приходится догадываться:) Два вызова api VarPtr сведут на нет всё, что можно наоптимизировать, т.к. эти два вызова потянут на очень много десятков тактов. А может и сотен. Имхо, остающиеся пару десятков написанных на асме оптимизировать не стоит. Скажем 200 тактов или 190 - невелика разница. Вот если бы избавиться от VarPtr, тем более, что это для временных переменных - было бы хорошо.
Я делал как-то поиск в строке подстроки (регистронезависимый), аналогия InString'а. Хорошо сохранился исходник, может подойдёт для твоего случая.

';===============================================
Instring proc Start:DWORD, StrAddr:DWORD, MatchAddr:DWORD, MatchLen:DWORD
    push esi
    push edi
    push ebx
    ';------------------------------------------
    mov ecx,Start    ';стартовая позиция (поиск с начала строки: Start=1)
    mov esi,StrAddr    ';начало строки
    @StartPos:        ';поиск нуля в куске строки от начало+старт.позиция
    cmp byte ptr [esi+ecx-1],0    ';до начала строки
    je @OutOfString    ';если встречен ноль - старт. позиция задана за пределами строки
    loopnz @StartPos
    ';-------------------------------------------
    cmp Start,0     ';"защита от дурака"
    jle @InvalidStart
    mov edx,MatchLen    ';кол-во символов, которое должно совпасть
    xor ecx,ecx
    dec Start
    add esi,Start    ';абсолютный стартовый адрес
    dec esi
    mov edi,MatchAddr    ';стартовый адрес MatchString
    xor eax,eax ;для выхода с eax==0
    align 16
    @Loop1:
        inc esi
    @Loop2:
        mov al,byte ptr[esi+ecx]
        mov al,byte ptr[offset Table+eax]
        mov bl,byte ptr[edi+ecx]
        mov bl,byte ptr[offset Table+ebx]
        test al,al    ';если строка просмотрена до конца
        jz @Ret    ';выход с нулём в eax
        ;----------------------------------------
        cmp al,bl    ';поиск совпадения
        je @Next
        xor ecx,ecx    ';если символы не совпадают, длина искомой цепочки=MatchLen
        mov edi,MatchAddr    ';Match-строку будем смотреть с начала
        jmp @Loop1
        @Next:    ';если символы совпали
            inc ecx
            cmp ecx,edx
            jne @Loop2
            add esi,ecx
            sub esi,edx    ';если "очередь совпадения" закончилась,
            ';вычисление позиции с которой начинается
            mov eax,StrAddr ';последовательность символов, совпадающая
            sub esi,eax    ';с MatchString
            inc esi
            mov eax,esi    ';выход. В eax- позиция вхождения
            jmp @Ret

@InvalidStart:
    mov eax,-1
    jmp @Ret
@OutOfString:
    mov eax,-2
@Ret:
    pop ebx
    pop edi
    pop esi
    ret

Instring endp



Это проверенный вариант, возвращает номер позиции от начала строки, т.е. "Моя СтрОкА" и "сТРоКа" вернет 5.

Вызывать можно так:
    ;DIM MyInclude AS STRING, MyMatch AS STRING
    ;DIM FindOffset AS DWORD
    MyInclude = "Длинная строка из файла, содержащего %WM_USER"
    MyMatch = "%WM_"
    FindOffset = Instring(1, BYVAL MyInclude, BYVAL MyMatch, 4)

ПБ-шную часть не проверял, в общем ф-ция ждет адреса строк.

Возвращенное значение можно где-нибудь запоминать, чтобы поиск в следующий раз выполнять не с начала инклюда, а с последнего найденного места.

Ответить

Номер ответа: 34
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #34 Добавлено: 17.03.05 14:43
Да, забыл про Table. Это байтовый массив (256 байт) для регистронезависимости. Можно делать "на лету" (как ты говорил в теме про сортировки TemplateString). И offset Table заменить на адрес этой строки (или массива байт).

Я использую свою таблицу:

.data
;------- таблица для case insensitive ----------
Table   db 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
        db  32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
        db  64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95
        db  96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
        db  128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159
        db 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
        db 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
        db 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255

Ответить

Номер ответа: 35
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #35
Добавлено: 17.03.05 15:28
 Вообще то да, догадаться тяжело. Да и твой код из МАСМА перевести в чистый ассемблер тоже не просто :).
 По поводу VarPtr - ну про количество тактов - это ты загнул :), но то что это функция ты прав. Правда функция эта состоит из одной инструкции (не считая конечно CALL и RET), но это всё не важно, т.к. если ты заметил, VarPtr в цикле не участвует.

Ответить

Номер ответа: 36
Автор ответа:
 ZagZag



ICQ: 295002202 

Вопросов: 87
Ответов: 1684
 Профиль | | #36 Добавлено: 17.03.05 15:37
cresta, а не будет оптимизации от замены .data на .const? В ВБ-то будет, а в
ассемблере...

Ответить

Номер ответа: 37
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #37
Добавлено: 17.03.05 15:43
Вот описание того что оно делает:
 Перед вызовом этой процедуры инклюд разбивается на две части. Указатели и длина каждой части передаются передаются сюда.

Параметры:

pInclude - указатель на первую половину инклюда
pInclude2 - указатель на вторую половину инклюда
IncLen - длина первой половины
IncLen2 - длина второй половины
pWordToSearch - указатель на искомое слово
pWordToSearch_Len - длина искомого слова.


Теперь код:

1. В EDX заносится в старшую половину LCase$(Left$(WordToSearch,2)), а в младшую соответственно Ucase. Т.е. первые 2 байта в верхнем и нижнем регистре.
2. В ECX - есессно :) длина (и ежу понятно - а куда ж ещё)
3. В ESI - указатель на 1-ю половину
4. В EDI - указатель на 2-ю половину
5. В EAX - указатель на предварительно размерянный массив Long
6. В EBX - индекс массива (ну того, который в EAX)

Ну а дальше всё просто, в проверяем последовательно слова(WORD) первой и второй половины и если совпадают - заносятся в массив (EAX+EBX*4)

 'If Ucase$(Left$(WordToSearch,2))=@ESI(ECX) Then
 ! CMP DX, WORD PTR [ESI]
 ! JE WrdFound
 'ElseIf Ucase$(Left$(WordToSearch,2))=@EDI(ECX) Then
 ! CMP DX, WORD PTR [EDI]
 ! JE WrdFound2
 ' Здесь вертим EDX на 16 бит чтобы в DX стало LCase.
 ! ROR EDX,16
 ' Соответсвенно проверяем на LCase
   ! CMP DX, WORD PTR [ESI]
   ! JE WrdFound_1
   ! CMP DX, WORD PTR [EDI]
   ! JE WrdFound2_1
 'Вертим обратно чтобы опять стало Ucase
 ! ROR EDX,16
 ' Ну это я думаю коментировать не надо :)
ContLoop:
 ! DEC ECX
 ! JZ Finish
 ! INC ESI
 ! INC EDI
 ! JMP WhileLen

 Ну а метки WrdFound - это занос позиции в заранее отведённый для этого массив.
 Ну вот и всё. Дальше вызывается процедура (Include_ScanEqu_Finish), которая вычищает мусор и остаются только подходящие под условие строки.

Всё это выполняется за доли секунды (включая чистку мусора). Прирост впечатляющий. Правда я пока код заполнения ListBox'а не делал. Может он всю оптимизацию и убьёт :)

Ответить

Номер ответа: 38
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #38 Добавлено: 17.03.05 16:00
ZagZag, .data и .const отличаются только правами, const - это readonly, data - readwrite.
По скорости разницы не будет. В секцию const писать нельзя, только читать, иначе программа упадёт.

А код перевести не так уж и сложно, пересчитать и заменить параметры, например StrAddr на dword ptr ss:[esp/ebp+???]. И всё. Ну с учетом того, что пушились три регистра. (+12)

Может он всю оптимизацию и убьёт :)
Наверняка так и будет :)
Замени на virtual listview, с ним правда сложнее, чем с листбоксом, и надо будет держать в памяти все куски строк, удовлетворяющие твоему InString, но зато это намного быстрее.

Ответить

Номер ответа: 39
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #39
Добавлено: 17.03.05 16:13
Хм, не знаю право стоит ли. В ListBox'е всё просто - LB_ADDSTRING и LB_FINDSTRING. А в листвью ещё и уведомления нужно обрабатывать, а я их не люблю почему то :) (да ComCtl.INC лишний раз подключать не хочется) . Попробую для начала ListBox, а если не получится - тогда уж ListView.

Ответить

Номер ответа: 40
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #40 Добавлено: 17.03.05 17:39
Сейчас переделываю прогу. Есть массив (строка) 16 Мб. Подготовить эту строку, скомпоновать в ней все данные в виде массива структур (50000 по 320 байт) занимает 300-400 мс. А уже все готовое по SendMessage запихать в листбокс или листвью - больше 30 сек. Просто 50000 раз SendMessage...
Поэтому решил вернуться к старому доброму virtual listview. Ему вообще ничего не надо записывать. Чтобы дать ему по запросу эти несколько строк, которые видны в его окне, времени практически 0 надо :)

Ответить

Номер ответа: 41
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #41
Добавлено: 17.03.05 17:43
Ну я вечером потестирую и скажу результаты. У меня скорее всего больше 500 элементов не будет.

Ответить

Номер ответа: 42
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #42
Добавлено: 17.03.05 17:46
Ты мне скинь сюда или на basicproduction@nm.ru модуль для Virtual ListView. Авось у меня такая проблема будет, не сейчас так потом когда нибудь.

Ответить

Номер ответа: 43
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #43
Добавлено: 18.03.05 11:00
ListBox повержен. Всё до примитивности просто.
Надеюсь что и в NT системах будет также.

Проверил на самом большом списке констант %ERROR_...(около 1500 элементов), загружает моментально.

Проверял только на глобальном массив строк, так что если с другими типами не получится - не обессудьте.

На входе LB_ADDSTRING стребует ASCIIZ, почему бы не превратить временно нашу наш многомегобайтный участок памяти в коретенькую строку типа ASCIIZ, а после добавления не вернуть её обратно? Патчится один байт в память до и после SendMessage.

А вот и сама процедура. На входе: Параметер1 - указатель на начало строки, Параметер2 - длина.

Sub ShowListBoxEqu_AddItem(ByVal ItemPos As Long, ByVal ItemLen As Long)
    Dim WH_S As SIZEL
    GetTextExtentPoint32 GetDC(hListBox), ByVal ItemPos, ItemLen, WH_S
    If WH_S.cX>Item_LargestWidth Then Item_LargestWidth=WH_S.cX
    Dim ToAddPtr As Asciiz Ptr
    ToAddPtr=ItemPos
    Dim PatchToZero As Byte Ptr
    PatchToZero=ItemPos
    @PatchToZero[ItemLen]=0
    SendMessage hListBox, %LB_ADDSTRING, 0, ByVal ItemPos
    @PatchToZero[ItemLen]=13
End Sub

Ответить

Номер ответа: 44
Автор ответа:
 sne



Разработчик Offline Client

ICQ: 233286456 

Вопросов: 34
Ответов: 5445
 Web-сайт: hw.t-k.ru
 Профиль | | #44
Добавлено: 18.03.05 11:40
Ну да скажем 1500 это явно маловато... я для клиента брал 20 000 записей...
Когда-то он загружался моментально, но были отормоза с отображением в ListView (данные прям из БД брались). Сейчас он пол года загружается, но хоть отображает сравнительно шустро...

Т.ч. надо бы брать больше...

PS
Как оказалось в VB очень тормознутый вызов АПИ, с одним челом брали код по ручному переводу юникодовой строки в ASCII, дык он в цикле Mid'ом делал в два раза библиотеку написанную мной на асьме...

А позже мы попробовали взять без цикла но большой объем данных - в итоге библа на асьме в 10 с лишним раз оказалась шустрей...

Так что чем меньше АПИ мы вызываем, тем получается быстрей что-ль...

Ответить

Номер ответа: 45
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #45
Добавлено: 18.03.05 12:19

Ну да скажем 1500 это явно маловато

 Забыл добавить что при копировании участка памяти в строку типа ASCIIZ перед добавлением, 1500 элементов загружались почти минуту. А здесь доли секунды. Прогресс ошеломляющий. О причинах только догадываюсь.


Как оказалось в VB очень тормознутый вызов АПИ

 Вообще то это для PB код. Если хочешь - потести для VB. Заодно и API ListBox свой проапгрейдишь если получится.

Ответить

Страница: 1 | 2 | 3 | 4 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам