· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› quicksort

<< . 1 . 2 . 3 . >>

Посл.отвђт Сообщенiе


Дата: Авг 9, 2004 19:49:34

Black_mirror
„while(1) это бесконечный цикл“
Понятно. У меня также бесконечный цикл был с выходом из середины цикла, а не в конце.
Я сейчас набросал этот алгоритм, сортирует, и быстро (на C:\ 39000 файлов, время сортировки по алфавиту имён - 390 мс - более чем удовлетворительно). Единственно где-то сидит глюк: при повторной сортировке (по отсортированной колонке или по несортированный соседней, алгоритм путает местами итемы в листвью. Буду смотреть, в чём дело. Спасибо за алгоритм :)


Дата: Авг 10, 2004 04:07:03

cresta
мне показалось, что с ним поиск быстрее
Показалось - это ничто. Проверить на практике не пробовал? Чтобы программа не замерзала в момент поиска и сортировки запускай их в отдельном потоке. Плюс, когда получаешь данные сразу выстраивай их в порядке согласно текущего критерия сортировки.

не надо заполнять LV в процессе поиска, можно только на LVN_GETDISPINFO выдавать
По-хорошему данные надо готовить, когда приходит LVN_ODCACHEHINT. Напиши простой тест и посмотри когда приходит LVN_ODCACHEHINT и LVN_GETDISPINFO.


Дата: Авг 10, 2004 11:50:08

q_q
„Проверить на практике не пробовал?“
Пробовал :)) В среднем в 8 - 10 раз быстрее. При этом и памяти для virtual listview столько не надо, соответственно, не надо отдельного потока. Основной тормоз при использовании обычного lv - необходимость в процессе поиска сразу вызывать SHGetFileInfo для получения иконы и передачи в lv.(наряду с получением размера файла,аттрибутов,даты). С virtual listview я в процессе поиска ничего не получаю, кроме имени и пути к файлу. Поэтому во много раз быстрее. А остальную инфу получаю из имени и пути по запросу LVN_GETDISPINFO (максимум для 10 итемов). Если взять крайний случай - поиск по '*' - на просмотр C:\ и заполнение lv требуется 145 сек против 13 сек для заполнения массива имен. И с памятью - lv c таким кол-вом итемов (39000) тянет 45-47 Мб, а массив гораздо меньше(39000 элементов по 420 байт).


Дата: Авг 10, 2004 12:08:28

cresta
Теперь понял. Ты используешь listview в режиме virtual, чтобы иметь возможность получать реквизиты не участвующие в сортировке по мере необходимости.

не надо отдельного потока ... инфу получаю из имени и пути по запросу LVN_GETDISPINFO
Ты хочешь сказать, что на каждый LVN_GETDISPINFO твой код дергает SHGetFileInfo и другие функции? Т.е. ты не знаешь что такое LVN_ODCACHEHINT?

памяти для virtual listview столько не надо
Сколько памяти надо для хранения реквизитов одного элемента?


Дата: Авг 10, 2004 14:40:35

q_q

„на каждый LVN_GETDISPINFO твой код дергает SHGetFileInfo “
Именно так. 10 раз на каждый LVN_GETDISPINFO (по количеству видимых итемов).
Для хранения данных об одном элементе надо 420 байт(5 подстрок:128-имя файла,11-размер, 5-аттр, 20-дата, 255-путь). В процессе поиска я кроме имени файла попутно получаю в WIN32_FIND_DATA размер,дату,аттрибуты. Раз эта инфа есть(на халяву), я её тут же перерабатываю и в массив пихаю,на скорость это практически не влияет, зато потом все готовое есть,и по GETDISPINFO я только копирую из массива готовые строки и передаю в lv. Единственно нужно вызвать SHGetFileInfo и от нее индекс иконки тоже передать в lv. Все достаточно быстро и безболезненно :) Основа всего - то, что единовременно требуется заполнить не более 10 итемов (независимо от общего количества элементов в массиве). А это происходит быстро.
Если же SHGetFileInfo вызывать в процессе поиска, то она морозит сам поиск. В зависимости от количества установленных для неё флагов на один её вызов уходит от 0,39 до 0,9 мс. И когда количество файлов найденных в цикле поиска велико, то поиск сильно затягивается.
„Т.е. ты не знаешь что такое LVN_ODCACHEHINT?“
Мне стыдно, но это так :((( Всё сразу узнать невозможно.


Дата: Авг 11, 2004 04:14:48

cresta
Для хранения данных об одном элементе надо 420 байт
Почему ты хранишь размер, атрибуты и дату в виде строк? Соответствующие преобразования занимают много времени?

Давай просвещу про LVN_ODCACHEHINT. Оно приходит до LVN_GETDISPINFO и сообщает, с какого по какой элемент необходимо приготовить информацию (вот тут возможно использовать поток), а затем начинают приходить LVN_GETDISPINFO для каждого элемента из указанного ранее диапазона. Когда происходит скроллинг, приходит новое LVN_ODCACHEHINT с новым диапазоном, но возможно частично совпадающим с предыдущим, поэтому необходимо получить информацию по новым элементам.

Не пробовал накапливать информацию об иконках: в каком файле, какой индекс, доставать ее (если такая иконка уже была получена, то этот пункт можно пропустить), создавать свой imagelist и постепенно помещать в него иконки, привязать его к listview в реквизитах элемента хранить индекс из imagelist, когда очередной раз придет LVN_GETDISPINFO останется только указать индекс из imagelist?


Дата: Авг 11, 2004 14:52:29

q_q
„Почему ты хранишь размер, атрибуты и дату в виде строк?“

Три причины:
1.Имя и путь получаю как строки, размер,атрибуты и дата - как числа. Если так же хранить, то надо 2 разные процедуры сортировки(для строк и для чисел)
2.Если хранить как есть, то экономия небольшая: 21 байт (на атрибутах - 1 байт,на ftLastAccessTime-13,на nFileSizeLow - 7).
3.Кроме того в любом случае преобразовывать в строки придется для передачи в item.pszText. Лучше делать это не дожидаясь запроса от lv, чтобы не морозить скролл.

„Оно приходит до LVN_GETDISPINFO и сообщает, с какого по какой элемент необходимо приготовить информацию “

Попробовал обрабатывать LVN_ODCACHEHINT - не понравилось. Если много итемов и резко потянуть скролл вниз, то большое количество сообщений LVN_ODCACHEHINT выстраиваются в такую очередь, что пока их всех обработаешь, даже с условием не обрабатывать однажды полученную иконку, проходит очень много времени и lv по нескольку секунд стоит пустой, до его обновления просто не доходит очередь. Возможно этого можно избежать, если на очередной LVN_ODCACHEHINT отменить обработку предыдущего LVN_ODCACHEHINT, то есть ликвидировать очередь, но как это сделать - не знаю... Вызвал процедуру получения иконки, и пока из неё программа не вернётся, не могу её прервать. Возможно отдельный поток по обработке поможет - не знаю, надо попробовать.


Дата: Авг 12, 2004 05:37:45

cresta
надо 2 разные процедуры сортировки(для строк и для чисел)
Теорию не знаешь. ;-)
Подпрограмма сортировки одна, а функции сравнения элементов разные, адрес функции сравнения задают как параметр подпрограммы сортировки.

Кроме того в любом случае преобразовывать в строки придется для передачи в item.pszText.
Ты же борешься за быстроту первой отрисовки, зачем тогда проводить преобразование всех элементов, если это можно делать по LVN_GETDISPINFO? Вдруг пользователь зашел в очередную папку, содержащую много элементов, чтобы сразу же перейти внутрь одной из первых. Твой listview всегда в режиме LVS_REPORT?

Попробовал обрабатывать LVN_ODCACHEHINT - не понравилось.
Хорошо. Попробую сам.


Дата: Авг 12, 2004 10:32:30 · Поправил: q_q

cresta
Попробовал я собирать статистику по уведомлениям LVN_ODCACHEHINT и LVN_GETDISPINFO. В том коде, который я посылал добавил
...
; !!! NMLVCACHEHINT нет в моем windows.inc
qqNMLVCACHEHINT struct
  hdr   NMHDR <>
  iFrom dd    ?
  iTo   dd    ?
qqNMLVCACHEHINT ends
...
.code
....
;----------------------------------------------------------;
align 4
sz_LVN_ODCACHEHINT_Format db 'LVN_ODCACHEHINT from=%d to=%d',0
align 4
DisplayNMLVCACHEHINT proc nmlvch:ptr qqNMLVCACHEHINT
  local buffer[256]:byte

  mov eax,nmlvch
  lea ecx,buffer
  push ecx
  invoke wsprintfA, ecx, offset sz_LVN_ODCACHEHINT_Format,\
        (qqNMLVCACHEHINT ptr [eax]).iFrom,\
        (qqNMLVCACHEHINT ptr [eax]).iTo
  call OutputDebugStringA
  ret
DisplayNMLVCACHEHINT endp

;----------------------------------------------------------;
align 4
sz_LVN_GETDISPINFO_Format db 'LVN_GETDISPINFO item=%5d subitem=%d mask=%04Xh '
                          db 'LVIF_TEXT=%1d LVIF_IMAGE=%1d',0
align 4
DisplayNMLVDISPINFO proc uses ebx, nmlvdi:ptr qqNMLVDISPINFO
  local buffer[256]:byte

  mov eax,nmlvdi
  xor ebx,ebx
  mov edx,ebx
  test (qqNMLVDISPINFO ptr [eax]).item.imask,LVIF_TEXT
  jz short @F
    inc ebx
  @@:
  test (qqNMLVDISPINFO ptr [eax]).item.imask,LVIF_IMAGE
  jz short @F
    inc edx
  @@:
  
  lea ecx,buffer
  push ecx
  invoke wsprintfA, ecx, offset sz_LVN_GETDISPINFO_Format,\
        (qqNMLVDISPINFO ptr [eax]).item.iItem,\
        (qqNMLVDISPINFO ptr [eax]).item.iSubItem,\
        (qqNMLVDISPINFO ptr [eax]).item.imask,\
        ebx, edx
  call OutputDebugStringA
  ret
DisplayNMLVDISPINFO endp

;----------------------------------------------------------;
align 4
OnNotify proc uses ebx, idCtrl:dword, pnmh:ptr NMHDR
   local buf[128]:byte

  .if idCtrl == ID_LISTVIEW
    mov ebx,pnmh
    mov eax,(NMHDR ptr [ebx]).code
    .if eax == LVN_ODCACHEHINT
      invoke DisplayNMLVCACHEHINT, ebx
    .elseif eax == LVN_GETDISPINFO
      invoke DisplayNMLVDISPINFO, ebx
      mov eax,(qqNMLVDISPINFO ptr [ebx]).item.iSubItem
      .if eax
        test (qqNMLVDISPINFO ptr [ebx]).item.imask,LVIF_TEXT
        jz short @F
          mov ecx,(qqNMLVDISPINFO ptr [ebx]).item.iItem
; !!! закомментировал, чтобы имя в ячейке совпадало с именем колонки
; !!!          inc ecx
          invoke wsprintf, addr buf, offset szItemFmt1, ecx,\
                   (qqNMLVDISPINFO ptr [ebx]).item.iSubItem
          invoke lstrcpy, (qqNMLVDISPINFO ptr [ebx]).item.pszText, addr buf
        @@:
      .else
        test (qqNMLVDISPINFO ptr [ebx]).item.imask,LVIF_TEXT
        jz short @F
          mov ecx,(qqNMLVDISPINFO ptr [ebx]).item.iItem
; !!! закомментировал, чтобы имя в первой колонке совпадало с номером строки
; !!!          inc ecx
          invoke wsprintf, addr buf, offset szItemFmt0, ecx
          invoke lstrcpy, (qqNMLVDISPINFO ptr [ebx]).item.pszText, addr buf
        @@:
        test (qqNMLVDISPINFO ptr [ebx]).item.imask,LVIF_IMAGE
        jz short @F
          mov (qqNMLVDISPINFO ptr [ebx]).item.iImage,0
        @@:
      .endif
    .endif
  .endif
  xor eax,eax
  ret
OnNotify endp
При старте программы отображаются строки с 0 по 13 и все 6 столбцов. В DbgView'е (www.sysinternals.com) вижу
LVN_ODCACHEHINT from=0 to=13
LVN_GETDISPINFO item=    0 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=    0 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=    0 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=    0 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=    0 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=    0 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=    0 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
... пропущу строки с 1 по 12 включительно, они копия 1-ой строки с очередным номером item'а
LVN_GETDISPINFO item=   13 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=   13 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
вопрос по второму вызову LVN_GETDISPINFO item=0 subitem=0 mask=10h для меня открыт, т.к. я не интересовался зачем существует LVIF_INDENT = 10h.

Нажимаю на нижнюю кнопку вертикальной полоски прокрутки
LVN_ODCACHEHINT from=13 to=14
LVN_GETDISPINFO item=   13 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=   13 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=   14 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   14 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
т.е. повторно запрашивается информация о 13-ой строке и информация о новой - 14-ой строке.

Если после старта нажать на область полоски прокрутки ниже thumb'а, т.е. потребовать сразу перейти на страницу ниже, то
LVN_ODCACHEHINT from=13 to=26
LVN_GETDISPINFO item=   13 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=   13 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   13 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
... пропущу строки с 14 по 25 включительно, они копия 13-ой строки с очередным номером item'а
LVN_GETDISPINFO item=   26 subitem=0 mask=0013h LVIF_TEXT=1 LVIF_IMAGE=1
LVN_GETDISPINFO item=   26 subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=   26 subitem=1 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   26 subitem=2 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   26 subitem=3 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   26 subitem=4 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
LVN_GETDISPINFO item=   26 subitem=5 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
т.е. поведение listview вполне предсказуемо.

А вот если шевелить мышкой над какой либо строкой, то начинаются чудеса. Постоянно приходят пары
LVN_GETDISPINFO item=  ### subitem=0 mask=0010h LVIF_TEXT=0 LVIF_IMAGE=0
LVN_GETDISPINFO item=  ### subitem=0 mask=0001h LVIF_TEXT=1 LVIF_IMAGE=0
где ###-номер строки над которой шевелится мышка, при этом колонка может быть любой.

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


Дата: Авг 12, 2004 11:48:40

Тоже смотрел, на какие итемы приходит запрос по LVN_ODCACHEHINT и по LVN_GETDISPINFO
Возможно, при шевелении мышой постоянный запрос одного и того же происходит по причине того, что lv не запоминает какой был последний запрошенный итем и не сверяет его с тем, над которым сейчас мышь, а просто запрашивает текущий и всё.
„Мне кажется что можно замутить постоянно существующий отдельный поток, который будет ждать диапазона.“
Т.е. чтобы поток постоянно крутился в холостую, ожидая определённых событий? Не знаю, хорошо ли это? Ведь нагрузка будет на процессор непроизводительная. А если по таймеру, то можно прозевать момент, если он попадёт между срабатываниями.

С сортировкой прошляпил, да. Действительно можно процедуры сравнения сделать разные, а остальное общее :)


Дата: Авг 12, 2004 13:02:34

cresta
lv не запоминает какой был последний запрошенный итем
Почему тогда lv хочет LVIF_INDENT и LVIF_TEXT?

Т.е. чтобы поток постоянно крутился в холостую ...
Есть куча средств для уведомления. От самопальных
; внутри потока
; пока done == 0 поток крутит цикл
.while done == 0
   .while flag == 0
    invoke Sleep, 0 ; Sleep гарантирует не 100% загрузку CPU
  .endw
; тут начинается сбор информации
  mov index,iFrom
; на каждой итерации сбора нужно проверять, не пора ли
; прекратить цикл, например, выставлен flag в ноль если
; необходимо изменить iFrom и iTo или
; done в не ноль если пора завершить программу
  .while (done == 0) && (flag != 0) && index <= iTo
    ...
    inc index
  .endw
.endw
ret
...
; в некотором месте программы
mov done,0
mov flag,0
invoke CreateThread, ...
mov hThread,eax
...
; когда понадобится разбудить поток
mov flag,1
...
; когда понадобится усыпить поток
mov flag,0
...
; когда пора завершить программу
mov done,1
invoke WaitForSihgleObject, hThread, INFINITE
...
до профессиональных InterLock-функции, Wait-функции, Event'ы.


Дата: Авг 12, 2004 14:00:07 · Поправил: cresta

„invoke Sleep, 0 ; Sleep гарантирует не 100% загрузку CPU“

попробовал так:
.while (1)
    mov eax,flag
    test eax,eax
    jnz @F
    invoke Sleep, 0 
.endw
@@:


Таскменеджер показывает 99% загрузки, правда, процесс не жадничает, и если запустить како-нить другой, требующий процессорного времени (например Виндовый поиск) то он охотно делится с ним, оставляет себе 20-25% и остальные 75-80% отдает. Т.е. не морозит систему, но проц нагружается. В принципе если поток не будет крутиться часами, то можно и так.


Дата: Авг 13, 2004 04:08:13

cresta
sorry. Sleep(10).
Я предупреждал, что это самопальный метод синхронизации. Еще, я не указал, что поток по звершению сбора информации должен обнулить flag.


Дата: Авг 13, 2004 17:29:57

q_q

Остановился на таком варианте: поиск - отдельным потоком, SHGetFileInfo - по LVN_GETDISPINFO (с запоминанием полученного индекса иконки, вторично не извлекаю, как ты и советовал, просто запомнил его).
Устраивать отдельный поток, параллельно с поиском, для SHGetFileInfo смысла нет, т.к. SHGetFileInfo - операция, требующая времени, а процессорное время - оно не резиновое, сделаешь два потока - время доступа к процессору потока, осуществляющего поиск, резко сокращается в пользу потока SHGetFileInfo. Поиск затягивается в 7-8 раз.
Ну вроде бы всё. Спасибо за участие и подсказки.


Дата: Авг 13, 2004 18:32:19


надо 2 разные процедуры сортировки(для строк и для чисел)
Теорию не знаешь. ;-)
Подпрограмма сортировки одна, а функции сравнения элементов разные, адрес функции сравнения задают как параметр подпрограммы сортировки.


Не утерплю. Внесу и я свои 5 копеек :)
q_q, ты дело говоришь, и в то же время - не дело. По хорошему, надо ДВЕ РАЗНЫЕ процедуры сортировки. И это будет правильнее, т.к. передача адреса функции тормозит программу. Если переписать qsort на С++ с использованием шаблона, просто, как есть, один к одному, то мы уже получим прирост производительности.

<< . 1 . 2 . 3 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.192