Поиск  Пользователи  Правила 
Закрыть
Логин:
Пароль:
Забыли свой пароль?
Регистрация
Войти
 
Страницы: 1
RSS
RegExp, скорость обработки данных п сравнению с обычными методами. Или я что-то делаю не то)
 
Понадобилось давеча повыковыривать числа из цифро-буквенной каши (массив цифро-буквенных данных на 10-15 т.элементов). А точнее первый блок цифр присутствующий в строке. Вспомнил , что неоднократно расхваливали RegExp за удобство и прочее, решил воспользоваться опытом товарищей. Что сказать... Действительно удобно, только почему-то ни разу не быстро. Может я что-то делаю не так, как нужно?

Функция по извлечению посредством RegExp:
Скрытый текст
Ее аналоги на стрингах и массивах:
Скрытый текст
Тестовый стенд:
Скрытый текст
Результат на моем не самом современном железе:
Скрытый текст
 
Цитата
Anchoret написал: Может я что-то делаю не так
Могу ошибаться, но, при прочих равных, ReqExp медленнее обработки массивов
Согласие есть продукт при полном непротивлении сторон.
 
Sanja,судя по тесту он медленнее всего. И это печально. Штука то хорошая.
 
Прошу прощения за вмешательство в беседу макрушников, но мне кажется это вечная противоположность Удобство./Универсальность - Скорость. Просто надо выбирать метод соответствующий задаче. Вспоминаем классику. Лучше день потерять, потом за пять минут долететь.
 
Цитата
БМВ написал:
Просто надо выбирать метод соответствующий задаче.
Так то оно так, но кто-же знал... Вера в чудо не умрет никогда.
------------
Возможно при раннем связывании с подключением соотв.библиотеки все будет шустрее. Только не уверен, что у людей для которых пишется код есть нужная библиотека или желание (возможность) ее подключать. Вот ссылка на нарытую инфу.
 
С другой стороны, регулярки становятся "межотраслевым" стандартом. Тот же Cisco Сall Manager работает с ними, так как проще заложить глобальный алгоритм и в качестве параметров принимать шаблон. Пусть скорость не та, но иначе сделать - означает просто заставить админа  системы писать скрипты , а это чревато непредсказуемым поведением системы. Вера в чудо на сегодня  это вера в то , что завтра процессор будет еще быстрее.  Это в далекие 80е, 90е для ускорения переписывали часть кода на ассемблере, сейчас, рекомендуют проапгрейдить железо :-(
 
Доброе время суток
Цитата
БМВ написал:
но мне кажется это вечная противоположность Удобство./Универсальность - Скорость.
Скорее в данном случае - это проблема реализации RegExp в данной библиотеке от Microsoft. Тестировал на C# поиск строк, начинающихся с пяти разных вариантов. RegEx раза в 3 быстрее, чем пять проверок String.StartsWith. Думаю, и поиск первой цифровой последовательности, уделает посимвольную проверку (ну, разве что, в данном конкретном случае перейти на байт массив и вести побайтовое сравнение).
Приблизительно тоже соотношение в движках Javascript (пробовал на Firefox и Chrome). Javascript, для автоматизации в ОС, скорее будет такой же медленный, как и VBScript.
Изменено: Андрей VG - 1 Апр 2018 18:19:32
 
Здравствуйте, коллеги! Ловкость рук, и никакого... Добавляем одно волшебное слово:
Код
Function NumExtr(txt$) As Boolean
Static RG As Object
Dim a%, FD As Object
If Len(txt) = 0 Then NumExtr = False: Exit Function
If RG Is Nothing Then
  Set RG = CreateObject("VBScript.RegExp")
End If
RG.Pattern = "\d+": RG.Global = True
If Not RG.test(txt) Then NumExtr = False: Exit Function
Set FD = RG.Execute(txt): NumExtr = True: txt = FD.Item(0)
End Function

и все результаты в тесте #1 одного порядка
Владимир
 
если стенд и одну функцию чуть поправить: (не инициироовать RegExp при каждом вызове функции, а передать его в функцию параметром)
Код
Sub ghjhfhfhhfy()
  Dim arr$(), dt$, a&, iTime#, re
  ReDim arr(1 To 100000)
  For a = 1 To UBound(arr)
    arr(a) = "asd-1234556GHJYYTRR-1567575"
  Next
  iTime = Timer
  For a = 1 To UBound(arr)
    NumExtrA arr(a)
  Next
  iTime = Timer - iTime
  Debug.Print "NumExtrA"; Tab; UBound(arr); Tab; Round(iTime, 3)
  iTime = Timer
  For a = 1 To UBound(arr)
    NumExtrB arr(a)
  Next
  iTime = Timer - iTime
  Debug.Print "NumExtrB"; Tab; UBound(arr); Tab; Round(iTime, 3)
  iTime = Timer
  Set re = CreateObject("VBScript.RegExp")
  re.Pattern = "\d+": re.Global = False
  For a = 1 To UBound(arr)
    NumExtr re, arr(a)
  Next
  iTime = Timer - iTime
  Debug.Print "NumExtr"; Tab; UBound(arr); Tab; Round(iTime, 3)
End Sub


Function NumExtr(re, txt$) As Boolean
  If Len(txt) = 0 Then Exit Function
  If re.test(txt) Then NumExtr = True: txt = re.Execute(txt)(0) Else Exit Function
End Function
то результаты ( на моем, видимо, еще более медленном железе)
NumExtrA       100000        0,547
NumExtrB       100000        0,391
NumExtr        100000        0,734
соизмеримы и вполне приемлемы.
Изменено: Ігор Гончаренко - 1 Апр 2018 18:46:39
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Честное слово, мы с Игорем не договаривались!
Владимир
 
Anchoret, а так?
Код
Function NumExtr(txt$) As Boolean
Static RG As Object, a%, FD As Object
If Len(txt) = 0 Then NumExtr = False: Exit Function
If RG Is Nothing Then
Set RG = CreateObject("VBScript.RegExp")
RG.Pattern = "\d+": RG.Global = True
End If
If Not RG.test(txt) Then NumExtr = False: Exit Function
Set FD = RG.Execute(txt): NumExtr = True: txt = FD.Item(0)
End Function
 
Цитата
Андрей VG написал:
проблема реализации RegExp в
то есть, возвращаемся к лени, недобросовестности, ... конкретных разработчиков конкретного инструмента, решения .... . Очень жаль, хотя это перекликается с
Цитата
БМВ написал:
Это в далекие 80е, 90е для ускорения переписывали часть кода на ассемблере, сейчас, рекомендуют проапгрейдить железо :-(
 
Цитата
БМВ написал:
то есть, возвращаемся к лени, недобросовестности
Михаил, не совсем так, просто в начале 2000 Microsoft в связи с конкуренцией с Java начала развивать .Net, соответственно, на ActiveX положили. В .Net, всё шустро, а ActiveX поддерживается только лишь бы работало по соображениям совместимости, но никто ничего там переписывать со стороны MS не будет. Это хорошо видно по VBA, с 97 ничего нового в плане языка. Вариантами или VSTO - это .NET либо лет 5 назад появилась возможность делать, начиная с 2013 на javascript - последнее позволяет писать код и для Excel Online. Жаль ссылки, которые давал Владимир ZVI по этой теме все умерли (там были готовые проекты с пошаговыми комментариями).
 
sokol92, Ігор Гончаренко, RAN, Спасибо :) Так действительно лучше. Значит найдется и RegExp для меня сфера применения)

П.С.: А ведь были подозрения насчет инициации объекта...
 
Насколько лучше?
 
RAN,на самом деле только вариант Игоря лучше:
Код
NumExtrA       100000        0,297 
NumExtrB       100000        0,289 
NumExtr        100000        0,633
 
У меня:

NumExtrA       100000        0,258
NumExtrB       100000        0,195
NumExtr         100000        0,547
Владимир
 
sokol92, видимо разные версии Excel с VBA. Проверял через Locals объект после выхода из функции всегда сбрасывается в Nothing (у меня Excel 2007).
 
Anchoret, функцию с RegExp можно оптимизировать.
1. и главное: если функция вызывается несколько раз, а тем более много раз, объект RG надо сделать статическим, чтобы он не создавался-стирался каждый раз при вызове функции.
2. Если нужен только первый блок цифр, не следует задавать RG.Global = True: это избавит от просмотра остатка строки после первого найденного фрагмента.
3. .Test и .Execute в какой-то степени делают одно и тоже - достаточно оставить .Execute.
Вот финальная версия функции и результаты на моем железе
Код
Function NumExtr1(txt$) As Boolean
Static RG As Object
  If Len(txt) = 0 Then Exit Function
  If RG Is Nothing Then
    Set RG = CreateObject("VBScript.RegExp")
    RG.Pattern = "\d+"
  End If
  With RG.Execute(txt)
    If .Count Then NumExtr1 = True: txt = .Item(0)
  End With
End Function
Код
NumExtr        100000        126,891 
NumExtr1       100000        4,125 'Static
NumExtr1       100000        3,641 ': RG.Global = True
NumExtr1       100000        3,141 'final
Функцию с массивом тоже можно оптимизировать, но это был бы оффтоп :)
 
Казанский, Спасибо!
Цитата
Казанский написал:
Функцию с массивом тоже можно оптимизировать
Это уж наверняка) Набросал ее до кучи. Есть повод пошевелить извилинами)

Немного оптимизированный набор на привычных операторах/функциях (оптимизация по кол-ву строк при работе с байтовыми массивами) + по методу от Казанского вариант с RegExp:
Скрытый текст
Результаты:
Скрытый текст
Изменено: Anchoret - 1 Апр 2018 20:00:42
 
я не акцентировал на этом внимание, но в 21-й строке кода аадал:
 re.Pattern = "\d+": re.Global = False
поиск до первого вхождения
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Anchoret, попробуйте вариант с "грязным приемчиком" :) У меня раза в 3 быстрее, чем на стрингах.
Код
Declare Sub GetMem2 Lib "msvbvm60" (src As Any, dst As Any)

Function NumExtrA1(txt$) As Boolean
Dim sb%, a&, b&, c&
  If Len(txt) = 0 Then Exit Function
  c = StrPtr(txt) + LenB(txt) - 2 'адрес последнего символа строки
  For a = StrPtr(txt) To c Step 2
    GetMem2 ByVal a, sb
    Select Case sb
    Case 48 To 57
      NumExtrA1 = True
      For b = a + 2 To c Step 2
        GetMem2 ByVal b, sb
        Select Case sb
        Case Is < 48, Is > 57
          Exit For
        End Select
      Next
      txt = MidB$(txt, a - StrPtr(txt) + 1, b - a)
      Exit Function
    End Select
  Next
End Function
 
Казанский, еще раз Спасибо :) Стараюсь не связываться с WinApi) И судя по тому, что прочитал про все эти указатели - не на всех версиях ОС + Excel они работают. Речь о х64 - на них не работает. А так конечно быстрее)
Изменено: Anchoret - 1 Апр 2018 20:28:46
 
Вдогонку - вариант с отображением целочисленного массива на строку NumExtrB1. Оказалось, что выигрыша по скорости по сравнению с NumExtrA1 практически нет (а возни гораздо больше).
И еще оказалось, что RegExp выигрывает, если в альтернативных функциях цикл пробегает >~150 раз, т.е. при такой длине подстроки до конца первого цифрового фрагмента. При длине >200 RegExp заметно быстрее!
весь код
Результат
Код
длина тестовой строки 27 
NumExtrA1      0,516 
NumExtrB1      0,5 
NumExtr1       2,844 
длина тестовой строки 127 
NumExtrA1      2,469 
NumExtrB1      2,406 
NumExtr1       3 
длина тестовой строки 227 
NumExtrA1      4,453 
NumExtrB1      4,281 
NumExtr1       3,141 
 
Казанский, есть предположение (завтра проверю), что с использованием в предварительном цикле функции InStr для стрингового варианта и InStrB для массива байтов будет быстрее даже на очень длинных строках. Цифр всего десять (отдельно взятых в числовом букваре), т.е. начало подстроки (если она есть) с цифрами найдем очень быстро, а далее циклом добить до первого вхождения любого другого символа.
А касательно RegExp видимо основное время затрачивается на запуск процедуры внутри объекта, а сам поиск и извлечение занимает мало времени.

Но это все о извлечении целых положительных чисел) С отрицательными почти также с доп.проверкой на символ перед первой цифрой. А вот всяческие десятичные дроби наверное будут сложнее. Не говоря о математических выражениях.
----------
Предположения не оправдались:
Код
NumExtrA      String Len: 10027            100000        123,312 
NumExtrB      String Len: 10027            100000        59,523 
NumExtrAplus  String Len: 10027            100000        10,027 
NumExtr       String Len: 10027            100000        1,805
1. перебирает все символы, склеивая цифры
2. тоже самое через байтовый массив
3. первичный поиск через InStr, потом склейка
4. RegExp

RegExp победил)

П.С.: Правда тест был не совсем корректен, т.к. массив 100000*10027(символов)=Out of string space, поэтому на каждом проходе из другой переменной восстанавливалось значение основной.

Врядли кому пригодится, но все-же :
Скрытый текст
Тестер:
Скрытый текст
Изменено: Anchoret - 3 Апр 2018 13:04:04
 
Цитата
Anchoret написал:
RegExp победил)
Думаю, что не Regexp победил, а язык "C" одержал очередную победу над (интерпретатором) VBA. И в этом нет ничего печального: каждому свое...
Владимир
 
Цитата
sokol92 написал:
И в этом нет ничего печального
Печально было, когда он проигрывал) Повторюсь - штука то полезная в хозяйстве. Всяко проще скормить RegExp строку закорючек и получить нужные данные, чем писать собственную процедуру по поиску и экстракту. Ну и опять таки плюс к карме тем программистам, что писали код для RegExp. Скорость ведь не только зависит от языка программирования и компиляции/интерпретации, но и от алгоритма заложенного в программу. Еще в начале этого года, когда домучивал свой сортер, столкнулся с тем, что прописанный в системной библиотеке код QuickSort выдает почти идентичные результаты по времени написанному на VBA, но с другим алгоритмом.
Изменено: Anchoret - 3 Апр 2018 18:46:52
Страницы: 1
Читают тему (гостей: 1)
Наверх