pyparsing - советы по производительности для обработки параллельных логов - PullRequest
1 голос
/ 29 августа 2011

Я использую пул из 2 процессов для параллельного анализа нескольких файлов журнала,

po = Pool(processes=2)
pool_object = po.apply_async(log_parse, (hostgroup_sender_dir, hostname, host_depot_dir,        synced_log, prev_last_pos, get_report_rate), )

(curr_last_pos, remote_report_datetime, report_gen_rate) = pool_object.get()

Однако это довольно медленный процесс при первом запуске, ~ 16 минут для примерно двенадцати ~ 20 МБ файлов.

В следующих итерациях проблем не будет, учитывая, что я буду анализировать логи новыми байтами каждые 2 или 3 минуты, но, безусловно, есть место для улучшения того, как я делаю это при первом запуске.Ускорит ли предварительное разбиение журналов на несколько сращиваний меньшего размера (чтобы pyparse не пришлось выделять весь журнал за унцию в памяти), это ускорит его?

Я все еще использую его на двойномcore dev VM, но вскоре придется перейти на четырехъядерный физический сервер (я постараюсь получить дополнительный четырехъядерный процессор), и, возможно, потребуется возможность управления ~ 50 журналами.

Aсращивание из бревна,

log_splice = """
# XX_MAIN     (23143) Report at 2011-08-30 20:00:00.003    Type:  Periodic     #
# Report number 1790                                        State: Active      #
################################################################################
# Running since                  : 2011-08-12 04:40:06.153                     #
# Total execution time           :  18 day(s) 15:19:53.850                     #
# Last report date               : 2011-08-30 19:45:00.002                     #
# Time since last periodic report:   0 day(s) 00:15:00.000                     #
################################################################################
                            ----------------------------------------------------
                            |       Periodic        |          Global          |
----------------------------|-----------------------|--------------------------|
Simultaneous Accesses       |  Curr  Max Cumulative |      Max    Cumulative   |
--------------------------- |  ---- ---- ---------- |     ---- -------------   |
Accesses                    |     1    5          - |      180             -   |
- in start/stop state       |     1    5      12736 |      180      16314223   |
-------------------------------------------------------------------------------|
Accesses per Second         |    Max   Occurr. Date |      Max Occurrence Date |
--------------------------- | ------ -------------- |   ------ --------------- |
Accesses per second         |  21.00 08-30 19:52:33 |    40.04  08-16 20:19:18 |
-------------------------------------------------------------------------------|
Service Statistics          |  Success    Total  %  |   Success      Total  %  |
--------------------------- | -------- -------- --- | --------- ---------- --- |
Services accepted accesses  |    17926    17927  99 |  21635954   21637230 -98 |
- 98: NF                    |     7546     7546 100 |  10992492   10992492 100 |
- 99: XFC                   |    10380    10380 100 |  10643462   10643462 100 |
 ----------------------------------------------------------------------------- |
Services succ. terminations |    12736    12736 100 |  16311566   16314222  99 |
- 98: NF                    |     7547     7547 100 |  10991401   10992492  99 |
- 99: XFC                   |     5189     5189 100 |   5320165    5321730  99 |
 ----------------------------------------------------------------------------- |
""" 

с использованием pyparse,

unparsed_log_data = input_log.read()

#------------------------------------------------------------------------
# Define Grammars
#------------------------------------------------------------------------
integer = Word( nums )

# XX_MAIN     ( 4801) Report at 2010-01-25 06:55:00
binary_name = "# XX_MAIN"
pid = "(" + Word(nums) + ")"
report_id = Suppress(binary_name) + Suppress(pid)

# Word as a contiguous set of characters found in the string nums
year = Word(nums, max=4)
month = Word(nums, max=2)
day = Word(nums, max=2)
# 2010-01-25 grammar
yearly_day_bnf = Combine(year + "-" + month + "-" + day)
# 06:55:00. grammar
clock24h_bnf = Combine(Word(nums, max=2) + ":" + Word(nums, max=2) + ":" + Word(nums,     max=2) + Suppress("."))
timestamp_bnf = Combine(yearly_day_bnf + White(' ') + clock24h_bnf)("timestamp")

report_bnf = report_id + Suppress("Report at ") + timestamp_bnf

# Service Statistics          |  Success    Total  %  | 
# Services succ. terminations |       40       40 100 |   3494775    3497059  99 |
partial_report_ignore = Suppress(SkipTo("Services succ. terminations", include=True))
succ_term_bnf = Suppress("|") + integer("succTerms") + integer("totalTerms")
terminations_report_bnf = report_bnf + partial_report_ignore + succ_term_bnf

# Apply the BNF to the unparsed data
terms_parsing = terminations_report_bnf.searchString(unparsed_log_data)

1 Ответ

2 голосов
/ 30 августа 2011

Я бы структурировал парсер вокруг разбора одной записи журнала. Это выполняет 2 вещи:

  1. разбивает проблему на легко распараллеливаемые куски
  2. позиционирует ваш синтаксический анализатор для обработки добавочной обработки журнала после того, как начальная задержка данных журнала была обработана

В таком случае размер распараллеливающегося куска представляет собой красиво упакованный отдельный элемент, и каждый процесс может анализировать элемент отдельно (при условии, что вам не нужно переносить информацию о состоянии или истекшем времени из одного сообщения журнала в другое). 1009 *

РЕДАКТИРОВАТЬ (этот вопрос превратился в еще одну тему о настройке pyparsing ...)

Я обнаружил, что лучше определять низкоуровневые примитивы, которые построены с использованием Combine(lots+of+expressions+here), используя выражение регулярного выражения Pyparsing. Обычно это относится к выражениям, таким как действительные числа или временные метки, например:

# 2010-01-25 grammar
yearly_day_bnf = Combine(year + "-" + month + "-" + day)
yearly_day_bnf = Regex(r"\d{4}-\d{2}-\d{2}")

# 06:55:00. grammar
clock24h_bnf = Combine(Word(nums, max=2) + ":" + 
                       Word(nums, max=2) + ":" + 
                       Word(nums, max=2) + Suppress("."))
clock24h_bnf = Regex(r"\d{2}:\d{2}:\d{2}\.")
clock24h_bnf.setParseAction(lambda tokens:tokens[0][:-1])

timestamp_bnf = Combine(yearly_day_bnf + White(' ') + clock24h_bnf)
timestamp_bnf = Regex(r"\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}:\d{2}")

Не нужно переусердствовать. Такие вещи, как integer=Word(nums), уже генерируют RE под прикрытием.

Обратите внимание, что я также удалил имя результатов из timestamp_bnf. Обычно я исключаю имена результатов из определений примитивов и добавляю их по мере того, как собираю их в выражения более высокого уровня, чтобы я мог использовать один и тот же примитив несколько раз с разными именами, например:

summary = ("Started:" + timestamp_bnf("startTime") + 
           "Ended:" + timestamp_bnf("endTime"))

Я считаю, что это также помогает мне организовать мои проанализированные структуры.

Перемещение имени результатов в более высокое выражение также побуждает меня дать полю более информативное имя:

report_bnf = report_id + Suppress("Report at ") + timestamp_bnf("reportTime")

Глядя на свою грамматику, вы на самом деле не взламываете всю информацию этого отчета, просто извлекаете время отчета из этой строки:

# XX_MAIN     (23143) Report at 2011-08-30 20:00:00.003

и 2 целых поля из этой строки:

Services succ. terminations |    12736    12736 100 |  16311566   16314222  99 |

Попробуйте вместо этого:

report_bnf = report_id + Suppress("Report at") + timestamp_bnf("reportTime")
succ_term_bnf = (Suppress("Services succ. terminations") + Suppress("|") + 
                        integer("succTerms") + integer("totalTerms"))
log_data_sources_bnf = report_bnf | succ_term_bnf

extractLogData = lambda logentry : sum(log_data_sources_bnf.searchString(logentry))

print extractLogData(log_slice).dump()

Pyparsing всегда будет медленнее чем RE, и может случиться так, что анализатор pyparsing в вашем случае - просто ступенька для прототипирования. Я уверен, что не смогу добиться производительности в 500 раз с помощью синтаксического анализатора, и вам, возможно, просто придется использовать решение на основе RE для обработки файлов журналов в мегабайтах.

...