Skip to content

perf: XPathパターンキャッシュと XPathMatchesInjectionResolver の per-spec キャッシュを追加#137

Merged
mitonize merged 2 commits into
develop-2.0from
perf/xpath-pattern-cache
May 20, 2026
Merged

perf: XPathパターンキャッシュと XPathMatchesInjectionResolver の per-spec キャッシュを追加#137
mitonize merged 2 commits into
develop-2.0from
perf/xpath-pattern-cache

Conversation

@mitonize

Copy link
Copy Markdown
Collaborator
  • XPathUtil._patternCache (ConcurrentHashMap): PatternParser.parse() を毎回呼び出していたのを 初回のみパースしてキャッシュするよう変更。XPath 式はアプリ起動後に固定のため安全。

  • XPathMatchesInjectionResolver: spec インスタンスへの _xpathInjectNodes キャッシュ導入

    • 毎ノードの spec 階層走査を削減(100ノード → 1回の構築)
    • per-spec キャッシュにより複数スレッドで共用。deprecate() でクリア。
    • XPATH_NAMESPACE を static 定数化(毎回 createNamespace() 廃止)
  • SpecificationImpl: isDeprecated() の存在チェックを 500ms 間隔に throttle

    • getExistsCheckIntervalMillis() で設定値をインスタンスフィールドに遅延キャッシュし 毎回の EngineSetting 参照と Long.parseLong() オーバーヘッドを除去

ベンチマーク結果 (BuildPerformanceTest、2000 iters):
inject_xpath (template-only): 0.592ms → 0.365ms (-38%)

- XPathUtil._patternCache (ConcurrentHashMap): PatternParser.parse() を毎回呼び出していたのを
  初回のみパースしてキャッシュするよう変更。XPath 式はアプリ起動後に固定のため安全。

- XPathMatchesInjectionResolver: spec インスタンスへの _xpathInjectNodes キャッシュ導入
  - 毎ノードの spec 階層走査を削減(100ノード → 1回の構築)
  - per-spec キャッシュにより複数スレッドで共用。deprecate() でクリア。
  - XPATH_NAMESPACE を static 定数化(毎回 createNamespace() 廃止)

- SpecificationImpl: isDeprecated() の存在チェックを 500ms 間隔に throttle
  - getExistsCheckIntervalMillis() で設定値をインスタンスフィールドに遅延キャッシュし
    毎回の EngineSetting 参照と Long.parseLong() オーバーヘッドを除去

ベンチマーク結果 (BuildPerformanceTest、2000 iters):
  inject_xpath (template-only): 0.592ms → 0.365ms (-38%)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitonize mitonize added this to the v2.0.0 milestone May 20, 2026
@mitonize

Copy link
Copy Markdown
Collaborator Author

isDeprecated の呼び出し内での実際の確認処理が行われる頻度を抑制するのは #83 にも寄与する。

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

XPath ベース injection のビルド処理を中心に、パース/探索/設定参照のコストをキャッシュで削減して性能改善する PR です。テンプレートビルド(特に injection フェーズ)で繰り返し発生する処理を、スレッドセーフなキャッシュや参照回数削減で最適化しています。

Changes:

  • XPathUtil に XPath パターン(PatternParser.parse)のグローバルキャッシュを追加
  • XPathMatchesInjectionResolver に spec 単位の m:xpath ノード探索結果キャッシュと static Namespace を追加
  • SpecificationImpl の isDeprecated() に存在チェックのスロットリングと設定値の遅延キャッシュを追加(+ベンチマーク用テスト追加)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/test/java/org/seasar/mayaa/functional/engine/BuildPerformanceTest.java injection フェーズの簡易ベンチマークを追加
src-impl/org/seasar/mayaa/impl/engine/specification/xpath/XPathUtil.java XPath Pattern の初回コンパイル結果をキャッシュして再利用
src-impl/org/seasar/mayaa/impl/engine/specification/SpecificationImpl.java isDeprecated の存在チェックをスロットリングし、XPath injection ノードキャッシュ用の保持領域を追加
src-impl/org/seasar/mayaa/impl/CONST_IMPL.java checkTimestampInterval 設定キーを追加
src-impl/org/seasar/mayaa/impl/builder/injection/XPathMatchesInjectionResolver.java spec 単位の injection 対象ノードキャッシュと Namespace の static 化

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 123 to 131
public boolean isDeprecated() {
if (_deprecated == false) {
long intervalMillis = getExistsCheckIntervalMillis();
long now = System.currentTimeMillis();
if (intervalMillis > 0 && (now - _lastExistsCheckMillis) < intervalMillis) {
return false;
}
_lastExistsCheckMillis = now;
_deprecated = _hasSource != isSourceExists();
Comment on lines +157 to +170
/**
* この spec の XPath injection ノードリストを返す。
* まだ構築されていない場合は {@code null} を返す。
*/
public List<SpecificationNode> getCachedXpathInjectNodes() {
return _xpathInjectNodes;
}

/**
* この spec の XPath injection ノードリストをセットする。
* 複数スレッドが同時に呼んでも内容は等価なので、上書きは許容する。
*/
public void setCachedXpathInjectNodes(List<SpecificationNode> nodes) {
_xpathInjectNodes = nodes;
Comment on lines 51 to 80
@@ -65,12 +70,16 @@ public static boolean matches(SpecificationNode test,
SpecificationNavigator.getInstance());
Context context = new Context(support);
try {
Pattern pattern = PatternParser.parse(xpathExpr);
Pattern pattern = _patternCache.computeIfAbsent(xpathExpr, expr -> {
try {
return PatternParser.parse(expr);
} catch (SAXPathException e) {
throw new RuntimeException(e);
}
});
return pattern.matches(test, context);
Comment on lines +32 to +60
private static final int WARMUP = 100;
private static final int ITERATIONS = 2000;

/**
* XPath ベースの injection (XPathMatchesInjectionResolver) の繰り返しビルド時間を計測。
*/
@Test
public void bench_inject_xpath() throws IOException {
final String TARGET = "/it-case/engine/inject_xpath/target.html";
runBench("inject_xpath (XPath cache)", TARGET);
}

/**
* ID ベースの injection (EqualsIDInjectionResolver) の繰り返しビルド時間を計測。
*/
@Test
public void bench_inject_mayaa() throws IOException {
final String TARGET = "/it-case/engine/inject_mayaa/target.html";
runBench("inject_mayaa (ID cache) ", TARGET);
}

/**
* XPath ベースの injection — テンプレートのみ deprecate(.mayaa は再読み込みしない)。
* injection フェーズ単体の性能を計測しやすい。
*/
@Test
public void bench_inject_xpath_templateonly() throws IOException {
final String TARGET = "/it-case/engine/inject_xpath/target.html";
runBench("inject_xpath (template-only) ", TARGET, false);
- SpecificationImpl._deprecated を volatile 化
  isDeprecated() のスロットリングでは _lastExistsCheckMillis(volatile)を
  更新後に _deprecated(非volatile)を更新していたため、他スレッドが新しい
  _lastExistsCheckMillis を見て早期 return しつつ古い _deprecated を読む
  恐れがあった。volatile 化により可視性を保証する。

- setCachedXpathInjectNodes() で Collections.unmodifiableList() でラップ
  getter が返すリストが外部から変更されないよう保護する。

- XPathUtil._patternCache を Caffeine キャッシュに変更(max 10,000、30分 TTI)
  ConcurrentHashMap の無制限成長を防ぐ。XPath 式が動的に生成される環境でも
  メモリを回収できるようにする。

- BuildPerformanceTest に @tag("test.PerformanceTest") を付与
  pom.xml の testExcludedGroups 設定により通常 CI では除外される。
  実行するには mvn test -P withPerformanceTest を使う。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitonize mitonize merged commit b099873 into develop-2.0 May 20, 2026
1 check passed
@mitonize mitonize deleted the perf/xpath-pattern-cache branch May 20, 2026 04:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants