Помогите оптимизировать работу с csv geo базой

Тема в разделе "WASM.ASSEMBLER", создана пользователем Flasher, 5 мар 2008.

  1. Flasher

    Flasher Member

    Публикаций:
    0
    Регистрация:
    31 янв 2004
    Сообщения:
    640
    Здравствуйте господа.
    Собственно нужно оптимизировать вот этот код:

    Код (Text):
    1. align 4
    2. Long2IP proc uses ecx ebx scr:dword,scrid:dword
    3.      local hIP1:dword
    4.      local hIP2:dword
    5.      local hIP3:dword
    6.      local hIP4:dword
    7.  
    8.        mov hIP1,0
    9.        mov hIP2,0
    10.        mov hIP3,0
    11.        mov hIP4,0
    12.        
    13.        mov eax,scr
    14.        and ebx,not 255
    15. ;----------------
    16.        movzx ebx,al
    17.        push eax
    18.        
    19.        mov eax,ebx
    20.        mov ecx,16777216
    21.        mul ecx
    22.        mov hIP1,eax
    23.  
    24.        pop eax
    25.        shr eax,8
    26.        
    27.        cmp scrid,1
    28.        je @F
    29. ;----------------
    30.        movzx ebx,al
    31.        push eax
    32.        
    33.        mov eax,ebx
    34.        mov ecx,65536
    35.        mul ecx
    36.        mov hIP2,eax
    37.  
    38.        pop eax
    39.        shr eax,8
    40.  
    41.        cmp scrid,2
    42.        je @F
    43. ;----------------
    44.        movzx ebx,al
    45.        push eax
    46.        
    47.        mov eax,ebx
    48.        mov ecx,256
    49.        mul ecx
    50.        
    51.        mov hIP3,eax
    52.  
    53.        pop eax
    54.        shr eax,8
    55.  
    56.        cmp scrid,3
    57.        je @F
    58. ;----------------
    59.        movzx ebx,al
    60.        push eax
    61.        
    62.        mov hIP4,ebx
    63.  
    64.        pop eax
    65.        shr eax,8
    66.     @@:
    67.        mov eax,hIP4
    68.        add eax,hIP3
    69.        add eax,hIP2
    70.        add eax,hIP1
    71.        ret
    72. Long2IP endp
    73. align 4
    74. GetCountry proc ipaddress:dword
    75.      local address:dword
    76.      local buffer[32]:byte
    77.      local iplen:dword
    78.      local ipcount:dword
    79.      local ip:dword
    80.  
    81.        invoke inet_addr,ipaddress
    82.         .if eax != -1
    83.            mov address,eax
    84.            mov ipcount,4
    85.         @C:
    86.            dec ipcount
    87.            invoke Long2IP,address,ipcount
    88.            mov ip,eax
    89.            invoke wsprintfA,addr buffer,$CTA0("%u"),ip
    90.            mov iplen,eax
    91.  
    92.            mov ecx,len
    93.            mov esi,buf
    94.            mov edi,esi
    95.        @L1:
    96.            mov al,[esi]
    97.            inc esi
    98.            or al,al
    99.            jz @L3
    100.            cmp al,0Ah
    101.            jne @L1
    102.            mov byte ptr [esi-1],0
    103.            jmp @L1
    104.        @L3:
    105.            mov edx,edi
    106.        @L2:
    107.            repnz scasb
    108.            pusha
    109. ;----------------------   strcmp
    110.            inc edx
    111.            xor ecx,ecx
    112.            mov ebx,iplen
    113.         @@:
    114.            mov al,byte ptr [buffer+ecx]
    115.             .if byte ptr [edx+ecx] == al
    116.                inc ecx
    117.                dec ebx
    118.                jnz @B
    119.                add edx,iplen
    120.                add edx,3
    121.                mov ebx,edx
    122.             @@:
    123.                mov al,[ebx]
    124.                inc ebx
    125.                or al,al
    126.                jz @F
    127.                cmp al,','
    128.                jne @B
    129.                mov byte ptr [ebx-1],0
    130.                add ebx,1
    131.                mov byte ptr [ebx+2],0
    132.                invoke lstrcpy,ipaddress,ebx
    133.                popa
    134.                xor eax,eax
    135.                ret
    136.             .endif
    137.         @@:
    138. ;----------------------
    139.            popa
    140.            repz scasb
    141.            mov edx,edi
    142.            dec edx
    143.            test ecx,ecx
    144.            jne @L2
    145.         .endif
    146.        
    147.         cmp ipcount,1
    148.         jne @C
    149.  
    150.         xor eax,eax
    151.         dec eax
    152.         ret
    153. GetCountry endp
    по скорости.

    Если кто не в курсе, там данные в таком виде:
    Код (Text):
    1. "2185625600","2185822207","US","USA","UNITED STATES"
    2. "2185822208","2185887743","DE","DEU","GERMANY"
    3. "2185887744","2185953279","US","USA","UNITED STATES"
    4. "2185953280","2186018815","DE","DEU","GERMANY"
    Пытался заменить wsprintfA на dwtoa, не получилось..
    Кроме wsprintfA ничего сверх тормозного там не вижу.
    Если не прав, тыкните носом :)
    Спасибо.
     
  2. dendi

    dendi New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2007
    Сообщения:
    233
    быстрее всего работают операции через eax же, тогда в начале лучше

    Код (Text):
    1. sub eax,eax
    2. mov hIP1,eax
    3. mov hIP2,eax
    4. mov hIP3,eax
    5. mov hIP4,eax
    хотя это мелочи :)
     
  3. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    Flasher
    Неужели эти две функции самые "тормозные" при работе с базой? :)
     
  4. Flasher

    Flasher Member

    Публикаций:
    0
    Регистрация:
    31 янв 2004
    Сообщения:
    640
    crypto, Ваша ирония тут не уместна, уважаемый.
     
  5. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    Flasher
    Поясни на словах, что должен делать твой код. Тогда можно будет говорить об оптимизации.

    На первый взгляд не понимаю следующее:
    1) Допустим invoke inet_addr,ipaddress вернул -1, тогда
    Код (Text):
    1. ...
    2.   local ipcount:dword  ;; !!! не инициализирован
    3.   local ip:dword
    4.  
    5.   invoke inet_addr,ipaddress
    6.   .if eax != -1
    7. ...
    8. @C:
    9. ...
    10.   .endif
    11.   cmp ipcount,1   ;; !!!
    12.   jne @C
    13. ...
    2) Что содержат buf и len? Адрес и длину буфера с базой geo? Тогда зачем на _каждой_ итерации в цикле по ipcount выполнять
    Код (Text):
    1. ...
    2.   mov ipcount,4
    3. @C:
    4.   dec ipcount
    5. ...
    6. ;; <-- begin
    7.   mov ecx,len
    8.   mov esi,buf
    9.   mov edi,esi
    10. @L1:
    11.   mov al,[esi]
    12.   inc esi
    13.   or al,al
    14.   jz @L3
    15.   cmp al,0Ah
    16.   jne @L1
    17.   mov byte ptr [esi-1],0
    18.   jmp @L1
    19. ;; <-- end
    20. ...        
    21.   cmp ipcount,1
    22.   jne @C
    23. ...
    3) Если я правильно понял, то имеем ip-адрес в виде строки "aaa.bbb.ccc.ddd", адрес которой передается в GetCountry в параметре ipaddress. При помощи inet_addr преобразуем его в число в виде 0xddccbbaa. Затем при помощи Long2IP переворачиваем его в 0xaabbccdd, тут непонятно назначение параметра scrid. Перед вызовом Long2IP есть dec ipcount, следовательно scrid никогда не будет больше трех и код после
    Код (Text):
    1. ...
    2. cmp scrid,3
    3. je @F
    4. ...
    никогда не получит управления.

    4) Собственно алгоритм переворачивания 0xddccbbaa в 0xaabbccdd, который реализован в Long2IP, очень неудачный. Можно использовать ws2_32.dll!ntohl, либо скопировать ее код себе, благо он тривиален.

    ps
    Если кто не в курсе, там данные в таком виде ...
    Например, в файле GeoIPCountryCSV.zip (1'476'084 байт) в таком виде (строки 35557 ... 35560)
    Код (Text):
    1. ...
    2. "130.70.0.0","130.72.255.255","2185625600","2185822207","US","United States"
    3. "130.73.0.0","130.73.255.255","2185822208","2185887743","DE","Germany"
    4. "130.74.0.0","130.74.255.255","2185887744","2185953279","US","United States"
    5. "130.75.0.0","130.75.255.255","2185953280","2186018815","DE","Germany"
    6. ...
     
  6. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    советую воспользоватся профайлером.
    плюс код long2ip заменить полностью.
     
  7. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    Flasher
    Это не ирония, уважаемый, а совет, облеченный в шутливую форму.
     
  8. Flasher

    Flasher Member

    Публикаций:
    0
    Регистрация:
    31 янв 2004
    Сообщения:
    640
    q_q
    Передаю GetCountry буффер с ip адресом в байтах 130.75.123.0
    Код (Text):
    1. invoke GetCountry,offset buffer
    построчно читаю буфер где данные из базы и проверяю совместимость, если совпадает, считываю эту строку до название страны, копирую в буфер, и с успешным возвращением выхожу из функции...

    По поводу проверки успешного возвращения inet_addr скосячил, там .endif ниже cmp ipcount,1 должен быть.
    угу
    Смотри, например если не найдет по 130.75.123.0, то будет проверять по 130.75.0.0, если тоже пусто, то по 130.0.0.0

    Перевод ip адреса в целое число в инете показано так:
    Код (Text):
    1. Here we used the formula (1) to compute the IP Number based on 24.24.24.24
    2. 404232216 = 16777216*24 + 65536*24 + 256*24 + 24
    Тут более подробно: http://www.maxmind.com/app/csv

    По поводу ntohl, да, выполняет те-же действия более оптимизировано чем моя функция.
    Код (Text):
    1.  
    2. ; содержимое ntohl
    3.       mov ecx,eax
    4.        mov edx,ecx
    5.        shl edx,10h
    6.        and eax,0FF00h
    7.        or eax,edx
    8.        mov edx,ecx
    9.        and edx,0FF0000h
    10.        shr ecx,10h
    11.        or edx,ecx
    12.        shl eax,8
    13.        shr edx,8
    14.        or eax,edx
    Но например если "130.75.123.0" в базе не найдется как я отсюда уберу 123 ? :) придется написать функцию которая будет считывать точки с конца строки потом пропустить это всё через inet_addr и опять > ntohl.

    В аттаче полный исходный код программы.
    А базу можете скачать отсюда http://ip-to-country.webhosting.info/node/view/6 весит 653кб.
     
  9. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    Flasher
    Но например если "130.75.123.0" в базе не найдется как я отсюда уберу 123
    После inet_addr и ntohl имеем нормальный dword. Чтобы убрать 123 достаточно сделать
    Код (Text):
    1. and ip, 0FFFF0000h
    только убирать ничего не надо, см. выделенное жирным.

    В аттаче полный исходный код программы ... базу можете скачать отсюда
    В базе, на которую ты указал, есть строка ""3740270592","3740925951","CN","CHN","CHINA"", что соответствует ""222.240.0.0","222.249.255.255","3740270592","3740925951","CN","China"". Твоя версия программы не найдет страну для адреса "222.242.1.1". Т.е. искать надо не по точному совпадению, а по попаданию в диапазон.

    Вернемся к оптимизации.
    Если операция поиска разовая, то твой подход с просмотром базы сверху вниз вполне подойдет.
    Если предполагается многоразовый поиск, то имеет смысл предварительно преобразовать базу, чтобы сравнивать dword’ы, а не строки.
     
  10. Flasher

    Flasher Member

    Публикаций:
    0
    Регистрация:
    31 янв 2004
    Сообщения:
    640
    q_q
    Благодарю за идеи!
    Мне многоразовый поиск нужен.
    Позаимствовал метод работы с массивами у Iczelion'а.
    Вбиваю в массив "3740270592","3740925951", в виде dword'ов, потом смотрю чтоб наш ип был равно или больще 3740270592 и равно либо меньще 3740925951.

    Думаю вполне удачно получилось, но я бы хотел чтоб ты взглянул если не занят ;)

    p.s. чтобы нижеприведенный код заработало, надо открыть ip-to-country.csv и добавить наверху строку [geo]
     
  11. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    Flasher
    я бы хотел чтоб ты взглянул
    Хорошо, что ты разбил решение задачи на два этапа. Первый - приготовиться к поиску и собственно поиск.

    Замечания:
    1) интересный способ использовать GetPrivateProfileSection чтобы прочитать файл в память и сразу расставить нуль-символы в концах строк. Imho он имеет тот недостаток, что код привязывается к конкретной ОС (а под 9x вообще существует ограничение на размер ini-файла). В AddGeoInfo происходит побайтный анализ строки, если добавить в нее пропуск "пока не конец строки", можно читать сразу из файла;
    2) обрати внимание, что во всех строках, кроме пяти первых, начало ip-адресов и сигнатуры страны начинаются с одной и той же позиции, т.е. можно не сканировать в поисках двойных кавычек (хотя для полной уверенности необходимо проверить это наблюдение);
    3) если не ориентироваться на подробности организации менеджера памяти той или иной ОС или той или иной rtl, то существует правило - просить память у ОС/rtl большим куском, разумеется, лучше сразу знать сколько надо. Например, для файла, содержащего 80094 диапазонов, потребуется 1281504 байт (4 ipstart + 4 ipend + 3 asciiz country + 5 dummy = 16), т.е. хватит буфера в половину размера файла;
    4) организация хранения диапазонов в виде списка приводит к тому, что во время поиска просматриваются все элементы начиная с первого. Насколько я заметил, то диапазоны в файле упорядочены (тоже надо проверять), если хранить диапазоны в виде массива, то искать можно методом половинного деления, что значительно сократит количество сравнений.

    Итого.
    Если предположить, что файл ip-to-country.csv меняется редко, можно создать из него ip-to-country.bin, в котором каждая запись 16 байт и диапазоны упорядочены. Использовать последний для поиска.