From cc106b4cff7f655557aee03bb40b61e516c29e47 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 15:51:49 +0900 Subject: [PATCH 1/2] =?UTF-8?q?CPU=E3=82=B3=E3=82=A2=E6=95=B0=E3=81=A7?= =?UTF-8?q?=E4=B8=A6=E5=88=97=E5=8C=96=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- run.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run.py b/run.py index d071c31..ea18bee 100755 --- a/run.py +++ b/run.py @@ -33,7 +33,9 @@ TARGET_PREFIX = [x[len('--prefix='):] for x in sys.argv[2:] if x.startswith('--prefix=')] TARGET_PREFIX = TARGET_PREFIX[0] if TARGET_PREFIX else '' CONCURRENCY = [int(x[len('--concurrency='):]) for x in sys.argv[2:] if x.startswith('--concurrency=')] -CONCURRENCY = CONCURRENCY[0] if CONCURRENCY else 2 +# 明示指定がなければ CPU コア数を使う。変換は CPU バウンドで実コア数まで +# ほぼ線形にスケールする (実測でデフォルト 2 に対し 16 コアで約 4.9 倍高速)。 +CONCURRENCY = CONCURRENCY[0] if CONCURRENCY else (os.cpu_count() or 2) if settings.CACHEBUST_TYPE == 'none': _CACHEBUST = '' From b15e9a7380ca2739e0c8fc4595a36096cc72303d Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 15:59:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A4=89=E6=8F=9B=E3=81=AE=E9=AB=98?= =?UTF-8?q?=E9=80=9F=E5=8C=96=E3=81=AE=E3=81=9F=E3=82=81=E4=BF=AE=E9=A3=BE?= =?UTF-8?q?=E3=82=92=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- markdown_to_html/qualified_fenced_code.py | 44 +++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/markdown_to_html/qualified_fenced_code.py b/markdown_to_html/qualified_fenced_code.py index bf0472a..d85cc4e 100644 --- a/markdown_to_html/qualified_fenced_code.py +++ b/markdown_to_html/qualified_fenced_code.py @@ -105,19 +105,28 @@ def _qualify_link(*xs): } -class Qualifier(object): +# 修飾行のパース用正規表現。QualifyDictionary のコマンド一覧は不変なので、 +# モジュール読み込み時に一度だけ構築し Qualifier ごとの再コンパイルを避ける。 +_QUALIFY_DICTIONARY = QualifyDictionary() +_QUALIFY_LINE_RE = re.compile( + r'^[ \t]*\*[ \t]+(?P.*?)(?P({commands})+)$'.format( + commands='|'.join(r'(\[{cmd}(\]|.*?\]))'.format(cmd=cmd) for cmd in _QUALIFY_DICTIONARY.qualify_dic))) - """修飾1個分のデータを保持するクラス""" +# 同一の修飾行から作られる Qualifier はコンパイル済み正規表現ごと使い回せる +# (target/commands は行文字列から決まる不変値で find_match は状態を持たない)。 +# GLOBAL_QUALIFY_LIST の各行は全コードブロックで共通なので、ここで共有すること +# でコードブロック単位に発生していた大量の正規表現コンパイルを削減する。 +# パースに失敗した行は None を記憶して再試行しない。 +_QUALIFIER_CACHE = {} - def __init__(self, line, qdic): - command_res = [r'(\[{cmd}(\]|.*?\]))'.format(cmd=cmd) for cmd in qdic.qualify_dic] - qualify_re_str = r'^[ \t]*\*[ \t]+(?P.*?)(?P({commands})+)$'.format( - commands='|'.join(command_res)) - qualify_re = re.compile(qualify_re_str) +class Qualifier(object): + + """修飾1個分のデータを保持するクラス""" + def __init__(self, line, qdic=None): # parsing - m = qualify_re.search(line) + m = _QUALIFY_LINE_RE.search(line) if not m: raise ValueError('Failed parse') self.target = m.group('target') @@ -156,19 +165,26 @@ def find_match(self, code): class QualifierList(object): def __init__(self, lines): - self._qdic = QualifyDictionary() + self._qdic = _QUALIFY_DICTIONARY - # Qualifier を作るが、エラーになったデータは取り除く + # Qualifier を作るが、エラーになったデータは取り除く。同一行はキャッシュ + # から共有し正規表現の再コンパイルを避ける (エラー行は None を記憶)。 def unique(xs): seen = set() results = [] for x in xs: - if x not in seen: - seen.add(x) + if x in seen: + continue + seen.add(x) + q = _QUALIFIER_CACHE.get(x, False) + if q is False: try: - results.append(Qualifier(x, self._qdic)) + q = Qualifier(x) except Exception: - pass + q = None + _QUALIFIER_CACHE[x] = q + if q is not None: + results.append(q) return results self._qs = unique(lines)