Почему whoosh совершает так медленно

Интересно, почему whoosh работает медленно со следующим кодом. Особенно коммит занимает довольно много времени.

Я пытался использовать limitmb=2048 с модулем записи вместо 128 по умолчанию, но это почти не имеет значения. В соответствии с предложениями я попробовал procs=3 для записи, что делает индексацию немного быстрее, но коммит еще медленнее. Также commit(merge=False) здесь не поможет, так как индекс пуст.

Я получаю такие результаты:

index_documents 12.41 seconds
commit 22.79 seconds
run 35.34 seconds

Что для такой маленькой схемы и примерно 45000 объектов кажется многовато.

Я тестировал с whoosh 2.5.7 и Python 2.7.

Это нормально и я просто слишком многого жду, или я что-то не так делаю?

Я также немного профилировал, и кажется, что whoosh пишет, а затем читает много солений. Кажется, это связано с тем, как обрабатываются транзакции.

from contextlib import contextmanager
from whoosh import fields
from whoosh.analysis import NgramWordAnalyzer
from whoosh.index import create_in
import functools
import itertools
import tempfile
import shutil
import time


def timecall(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        start = time.time()
        result = f(*args, **kw)
        end = time.time()
        print "%s %.2f seconds" % (f.__name__, end - start)
        return result
    return wrapper


def schema():
    return fields.Schema(
        path=fields.ID(stored=True, unique=True),
        text=fields.TEXT(analyzer=NgramWordAnalyzer(2, 4), stored=False, phrase=False))


@contextmanager
def create_index():
    directory = tempfile.mkdtemp()
    try:
        yield create_in(directory, schema())
    finally:
        shutil.rmtree(directory)


def iter_documents():
    for root in ('egg', 'ham', 'spam'):
        for i in range(1000, 16000):
            yield {
                u"path": u"/%s/%s" % (root, i),
                u"text": u"%s %s" % (root, i)}


@timecall
def index_documents(writer):
    start = time.time()
    counter = itertools.count()
    for doc in iter_documents():
        count = counter.next()
        current = time.time()
        if (current - start) > 1:
            print count
            start = current
        writer.add_document(**doc)


@timecall
def commit(writer):
    writer.commit()


@timecall
def run():
    with create_index() as ix:
        writer = ix.writer()
        index_documents(writer)
        commit(writer)


if __name__ == '__main__':
    run()

person fschulze    schedule 17.06.2014    source источник


Ответы (1)


В коммите происходит какое-то слияние сегментов; это также объясняет, почему procs=3 делает работу еще медленнее (больше сегментов для слияния!).

Для меня решением было установить multisegment=True, как было предложено здесь.

writer = ix.writer(procs=4, limitmb=256, multisegment=True)

Вы можете настроить свои procs и limitmb по своему усмотрению, но учтите, что limitmb за проки! (т.е. они умножаются)


Предостережение: есть штраф в отношении скорости поиска. Например:

  • 10000 документов: ~200ms (w/o multisegment) vs 1.1secs (with multisegment)

  • 50000 документов: ~60ms (w/o multisegment) vs ~100ms (with multisegment)

Примерно на 40% медленнее в моей системе только на commit. Я не измерял время индексации, но multisegment также намного быстрее.

Это может быть решением для прототипирования. Как только вы узнаете, что у вас есть нужные Schema и параметры, вы можете установить multisegment обратно на False и запустить его снова.

просто чтобы дать примерное представление о

person toto_tico    schedule 29.05.2020