|
|
| Посл.отвђт | Сообщен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 на С++ с использованием шаблона, просто, как есть, один к одному, то мы уже получим прирост производительности. |
|
Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.192 |