Как стать автором
Обновить

Комментарии 14

Автор, конечно можно. Вот только это вы должны были нам рассказать как именно.
Есть ощущение что если надо просто посчитать частоты тегов, то можно и без xml парсера обойтись. Возможно это будет быстрее.
Что еще можно попробовать:
— раззиповать все или по очереди, но не питоном
— попробовать не создавать парсер для каждого файла, возможно проканает (надо проверять)
— парсить не все, а подмножетство, например 10% от библиотеки

Хотя лично я думаю, что для разовой оценочной задачи можно часок и подождать :)
А быстрее — можно?

А вдруг вы просто уперлись в HDD или в CPU?
Конечно уперся. в CPU одного процессора. Не забываем про GIL Пайтона и то что таких программ должно быть запущено минимум 2 (в кол-во ядер процессора). Тогда можно и ускориться.
Я бы предложил под термином «быстрее» понимать «эффективнее» — кол-во перемолоченных байт на мегагерцеядро.
Я пересчитал в такты процессора.
Я бы сделал параллельное выполнение (можно даже в лоб — на bash). Делим входные файлы на сегменты по количеству ядер (например и у нас 4000 файлов и 4 ядра — значит делим на 4 группы по 1000 файлов) и на каждую кучу натравливаем свой парсер.
А зачем переводить fb2 в docbook?
Формат docbook предназначен в первую очередь для технических публикаций, есть тэги для вставок схем, примеров исходного кода, при этом читать его практически нечем на большинстве устройств, при этом можно автоматически конвертировтать в pdf, html и т.д. Fb2 расчитан на нетехнические тексты, зато читать его можно на почти на любом устройстве. Боюсь, что такое преобразование не имеет никакой практической пользы.
Вашей статье, если ваша цель таки найти более быстрое решение, место на Codereview.SE. Там и посоветуют и оценят и обсудят.
вам бы хватило лексера без парсера. Кстати, интересно — xml.parsers.expat преобразует XML Entities? Если да, то при значительном их количестве производительность может просесть очень сильно из за множеств копирований памяти.

Вместо
def start_element(name, attrs):
    tags[name] = tags[name] + 1 if name in tags else 1


посоветовал бы использовать

def start_element(name, attrs):
    try:
        tags[name] += 1
    except KeyError:
        tags[name] = 1
Или вообще collections.Counter().
Это потому что вероятность НЕ встретить тег в таблице гораздо меньше вероятности встретить. Так что эксепшны будут выпадать крайне редко (в основном в начале), зато избегаем лишнего поиска по хеш таблице if name in tags. Учитывая то, что эта функция вызывается крайне часто, можно получить существенный прирост скорости.
1. если речь идет о пойти на рекорд — то да, хватит даже grep'а. Но это только начало. Дальше надо будет анализировать библиотеку, составлять индексы и всё такое. Т.е. нужен именно sax-парсер.
2. try-except немного помогло, спасибо за идею.
Не проверял на скорость, но я в таких случаях использую defaultdict:

from collections import defaultdict
…
tags = defaultdict(lambda: 0)
…
def start_element(name, attrs):
    tags[name] += 1

Даже в первом варианте работа триггера занимает 8% времени. А 40% — таки сам парсер.
Но за подсказку спасибо
Здесь главный вопрос не скорость. Автосоздание переменных в таком виде зачастую весьма удобно: если проигнорировать импорт, то это одна короткая и понятная строчка (определение tags не считается, так как должно присутствовать в обоих случаях) против либо длинной, либо и вовсе четырёх. Читаемость везде нормальная, но если конструкций с except KeyError: много, то, если скорость позволяет, уж лучше использовать ваш вариант.

Кстати, вспомнил про аналогичный benchmark из этой темы. Если воспользоваться кодом оттуда (но файлом увеличенным в десять раз простым «умножением»), то распределение по скорости выглядит следующим образом (от самого быстрого: 3,3, 3,4, 3,7, 4,1):

d = defaultdict(int)
…
    d[x] += 1

try:
    d[x] += 1
except KeyError:
    d[x] = 1

if x in d:
    d[x] += 1
else:
    d[x] = 1

d[x] = d[x] + 1 if x in d else 1

Собственно весь код:
import re

n = 5
with open("./pg2600.txt", "r") as f:
    s = f.read()
d={}
for x in re.findall(r'\w+',s):
    <tested code here>
print repr([k for k,v in d.items() if v == n])

Мой вариант стабильно быстрее except KeyError: (разница мала или очень мала, но он всегда быстрее), последние два могут показывать близкую производительность (но ваш всегда медленнее).

Замечу, что здесь используется int, а не lambda: 0. Разница в скорости, впрочем, не обнаружена.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории