LINUX.ORG.RU

История изменений

Исправление ados, (текущая версия) :

Спасибо всем за ответы. В результате снижение использования регулярок и, в особенности, перевод на множества дали заметное, но небольшое ускорение. Последнее значительно улучшило читаемость кода.

Но сильнее ускорил переход с difflib на rapidfuzz, благо последний получилось собрать на android. Получилось:

#!/usr/bin/env python

import sys, re, os
from pathlib import Path
from rapidfuzz import process as fuzz
from rapidfuzz import fuzz as fuzzmethod


def regexp_compile (r):
    return re.compile(r, re.IGNORECASE)

def words_generator (buff):
    length = len(buff)
    start = 0
    while start < length:
        x = buff.find(b'\x00', start)
        yield buff[start:x].decode('utf-8')
        start = x + 9 # 1 byte for \0 + 4 bytes * 2 
        ## 32 bit for word index and 32 bit for word length


class RegexAccumulator:
    def __init__ (self, regexp):
        self.collection = set()
        self.regexp = regexp_compile(regexp)

    def accumulate (self, words):
        self.collection.update(s for s in words if self.regexp.fullmatch(s))

    def result (self):
        result = sorted(self.collection)
        result.reverse()
        return result


class UsualAccumulator:
    def __init__ (self, word):
        self.word = word
        self.lword = word.lower()
        self.wlen = len(word)
        self.rset = set()
        self.dm_set = set()

        self.breg = regexp_compile(re.escape(word))

    def accumulate (self, words):
        self.rset.update(s for s in words if self.lword in s.lower())
        m = map(lambda x: x[0],
                fuzz.extract(self.word, words,
                             scorer=fuzzmethod.QRatio,
                             score_cutoff=70,
                             limit=400))

        self.dm_set.update(m)

    def result (self):
        result = list()
        self.dm_set.difference_update(self.rset)
        set2 = set()
        set2.update(s for s in self.rset if self.breg.match(s))
        self.rset.difference_update(set2)

        if self.word in set2:
            result.append(word)
            set2.discard(word)

        result.extend(sorted(set2))
        result.extend(sorted(self.rset))
        result.extend(sorted(self.dm_set))

        result.reverse()
        return result


if __name__ == "__main__":

    word = sys.argv[1]
    if word == "-r" and len(sys.argv) > 2:
        accumulator = RegexAccumulator(sys.argv[2])

    else:
        accumulator = UsualAccumulator(word)

    files = Path(os.environ['STARDICT_DATA_DIR']).rglob("*.[iI][dD][xX]")

    for name in files:
        with open(name, 'rb') as f:
            ## My biggest .idx file ~ 11Mb so ...
            buff = f.read()
        accumulator.accumulate(list(words_generator(buff)))

    for word in accumulator.result():
        print(word)

На тесте перевод на множество сократил время работы с 4 минут до чуть более 3 минут, а rapidfuzz довёл время выполнения до ~ 35 сек.

Исходная версия ados, :

Спасибо всем за ответы. В результате снижение использования регулярок и, в особенности, перевод на множества дали заметный, но небольшое ускорение. Последнее значительно улучшило читаемость кода.

Но сильнее ускорил переход с difflib на rapidfuzz, благо последний получилось собрать на android. Получилось:

#!/usr/bin/env python

import sys, re, os
from pathlib import Path
from rapidfuzz import process as fuzz
from rapidfuzz import fuzz as fuzzmethod


def regexp_compile (r):
    return re.compile(r, re.IGNORECASE)

def words_generator (buff):
    length = len(buff)
    start = 0
    while start < length:
        x = buff.find(b'\x00', start)
        yield buff[start:x].decode('utf-8')
        start = x + 9 # 1 byte for \0 + 4 bytes * 2 
        ## 32 bit for word index and 32 bit for word length


class RegexAccumulator:
    def __init__ (self, regexp):
        self.collection = set()
        self.regexp = regexp_compile(regexp)

    def accumulate (self, words):
        self.collection.update(s for s in words if self.regexp.fullmatch(s))

    def result (self):
        result = sorted(self.collection)
        result.reverse()
        return result


class UsualAccumulator:
    def __init__ (self, word):
        self.word = word
        self.lword = word.lower()
        self.wlen = len(word)
        self.rset = set()
        self.dm_set = set()

        self.breg = regexp_compile(re.escape(word))

    def accumulate (self, words):
        self.rset.update(s for s in words if self.lword in s.lower())
        m = map(lambda x: x[0],
                fuzz.extract(self.word, words,
                             scorer=fuzzmethod.QRatio,
                             score_cutoff=70,
                             limit=400))

        self.dm_set.update(m)

    def result (self):
        result = list()
        self.dm_set.difference_update(self.rset)
        set2 = set()
        set2.update(s for s in self.rset if self.breg.match(s))
        self.rset.difference_update(set2)

        if self.word in set2:
            result.append(word)
            set2.discard(word)

        result.extend(sorted(set2))
        result.extend(sorted(self.rset))
        result.extend(sorted(self.dm_set))

        result.reverse()
        return result


if __name__ == "__main__":

    word = sys.argv[1]
    if word == "-r" and len(sys.argv) > 2:
        accumulator = RegexAccumulator(sys.argv[2])

    else:
        accumulator = UsualAccumulator(word)

    files = Path(os.environ['STARDICT_DATA_DIR']).rglob("*.[iI][dD][xX]")

    for name in files:
        with open(name, 'rb') as f:
            ## My biggest .idx file ~ 11Mb so ...
            buff = f.read()
        accumulator.accumulate(list(words_generator(buff)))

    for word in accumulator.result():
        print(word)

На тесте перевод на множество сократил время работы с 4 минут до чуть более 3 минут, а rapidfuzz довёл время выполнения до ~ 35 сек.