<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nayuta</title><link>https://blog.ast.moe/tags/nayuta/</link><description>最近の記事: 五里霧中</description><generator>Hugo</generator><item><title>Omoikane開発日記 AIの検証待ちと承認待ちを整理した日</title><link>https://blog.ast.moe/blog/2026-11-05/</link><pubDate>Thu, 05 Nov 2026 18:14:41 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-11-05/</guid><description>&lt;p&gt;今日は、Omoikaneのコードを書き進めるだけではなく、AIたちが作った変更をどう検証し、どこまで終わったら取り込むかを考えていた。実装案は増える。レビューの報告も増える。それでも、mainに入る変更が同じ速さで増えるとは限らない。&lt;/p&gt;
&lt;p&gt;朝に状況を確認した時点では、未マージのPRが27件あった。そのうち18件がDraftだった。保存用のPRも含まれるので、27件全部を急いでマージすればいいわけではない。しかし、個々の担当が仕事を進めているのに、統合までの流れが細くなっているのは気になった。&lt;/p&gt;
&lt;p&gt;最終的には、CIのフォント準備の改善と正規表現の修正がマージされ、拒否されたWorkerの無駄な初期化を防ぐ変更もDraft PRになった。長く追っているiframe保持テストについては、ようやくbootstrapの内訳を一度測れた。運用側では、ブラウザから判断を返す承認UIを作った。&lt;/p&gt;
&lt;p&gt;この記事は2026年11月5日18時06分ごろまでのroomの報告と、執筆時に確認したGitHubの状態を基にしている。参加端末のハンドルネームは伏せ、実装担当、検証担当、調整担当という役割で書く。&lt;a href="https://blog.ast.moe/blog/2026-11-04/"&gt;前日の記事&lt;/a&gt;がGCの改善候補と解析ツールの話だったので、今日はそこから先の、検証と判断を回す仕組みの話になる。&lt;/p&gt;
&lt;h2 id="作業が進んでいることと統合が進んでいること"&gt;作業が進んでいることと統合が進んでいること&lt;/h2&gt;
&lt;p&gt;cc-sakuraのroomを見ると、各担当はかなり細かく動いている。修正案の準備、テストの実行、実行ログの保存、別担当による確認。そのどれも必要な仕事だが、投稿数だけで進捗を判断すると、実際より順調に見えることがある。&lt;/p&gt;
&lt;p&gt;一つのPRについて、対象テストは通っていても全体テストは終わっていない場合がある。全体テストが通っていても、CIの特定の環境では失敗することがある。あるいは、その失敗自体は以前から知られていても、確認に必要な結果ファイルが保存されず、残りの条件を確かめられないことがある。&lt;/p&gt;
&lt;p&gt;今日も、MacのGate5で起きる既知の時間制限が複数のPRに影響していた。一方で、すべてをその問題だけで説明できるわけではない。ローカルに別の失敗を残しているPRや、前提となるPRがまだ取り込まれていない変更もあった。表面上はどれも赤いチェックだが、止まり方は違う。&lt;/p&gt;
&lt;p&gt;私が知りたかったのは、誰が忙しいかということより、どの条件が満たされれば次へ進むのかだった。そこが曖昧なままだと、担当者は調査を続けられるし、新しい課題もいくらでも見つかる。けれども、古いPRの完了は後回しになる。&lt;/p&gt;
&lt;p&gt;そこで、古いCI改善や正規表現のPRを完了させる流れを優先し、新しい案は別の作業領域で準備するところまでにする場面があった。手が空いているから新しいPRを増やす、というだけでは解消しない詰まり方だった。&lt;/p&gt;
&lt;h2 id="重い検証が一台に集まっていた"&gt;重い検証が一台に集まっていた&lt;/h2&gt;
&lt;p&gt;重い全体検証は、メモリとストレージに余裕があるLinuxのx86環境へ集まっていた。小さい環境で全体テストを走らせると、プロセスがSIGKILLで終わり、最後の集計まで到達しないことがあったためだ。&lt;/p&gt;
&lt;p&gt;大きい環境へ委譲する判断そのものは合理的だった。ただ、その結果として、複数の案件が同じ列に並ぶ。全体テストを実行し、その後に通常ビルドを確認する。それが終わるまで次の案件は始めない。依存のビルドからやり直す場合には、待ち時間も長い。&lt;/p&gt;
&lt;p&gt;別の環境にはCPUの余裕があったので、もう少し評価を引き受けられるのではないかと聞いた。調べてもらうと、CPUが空いていることと、検証を始められることは別だった。その環境はLinuxのARMで、メモリは約8GiB、swapはなく、ビルド用領域の空きは約8GiBしかなかった。&lt;/p&gt;
&lt;p&gt;一方、ソースを置く共有領域には空きがあった。しかし、そちらはVirtioFS経由で、ビルド用のguardが実行権限の問題を理由に拒否する構成だった。空いているディレクトリを指定するだけでは解決しなかった。&lt;/p&gt;
&lt;p&gt;CPU、メモリ、ディスクの空き、ファイルシステムの性質は、別々に確認しないといけない。さらに、ハンドル名から実行OSやCPUを推測するのも危ない。今回も、Mac側の環境と物理的な資源を共有していても、実際に検証を回す中身はLinuxのARMだった。&lt;/p&gt;
&lt;h2 id="キャッシュを掃除して第二の評価環境を作る"&gt;キャッシュを掃除して第二の評価環境を作る&lt;/h2&gt;
&lt;p&gt;容量を調べると、完了済みの作業に使った約40GiBのビルドキャッシュが見つかった。ただし、ビルド成果物には、後から失敗を調べ直すためのテストバイナリが含まれている。全部を一括で消すと、結果の根拠まで失う可能性がある。&lt;/p&gt;
&lt;p&gt;そこで、対象のキャッシュを別のファイルシステムへ圧縮退避し、元の内容と照合してから整理する流れになった。ソースのworktreeやブランチ、ログは残し、削除対象を一つのtargetに限定する。空き容量も、丸めた表示だけでなく実際のbytesで確かめた。&lt;/p&gt;
&lt;p&gt;回収後は約48GiBまで空きが戻ったが、それでも開始条件のぎりぎりだった。表示が「48G」だから大丈夫とは限らず、ほかの担当が少し書き込くだけで下回る。途中では数MiB足りずに開始を見送る場面もあった。&lt;/p&gt;
&lt;p&gt;最終的に、この環境は正規表現修正のengine側の評価を引き受けた。関係するengine群で2,044件、正規表現の対象で11件が成功し、合計2,055成功、失敗0、除外6件だった。新しく追加したcanonicalizationの回帰テストも成功した。全体テストは別のx86環境に残したまま、切り分けられる評価を並行して進められたことになる。&lt;/p&gt;
&lt;p&gt;これはマシンを一台追加した話ではない。既存の資源を整理して、CPUが空いている環境を実際の評価に使える状態へ戻した話だ。ただし、全体検証を同じように任せられるほどメモリが増えたわけではない。その環境に合う範囲を割り当てる必要は残る。&lt;/p&gt;
&lt;h2 id="手元のmacも掃除した"&gt;手元のMacも掃除した&lt;/h2&gt;
&lt;p&gt;room側の整理とは別に、手元のMacにも使い終わった生成物がたまっていた。ディスクの空きは約53GiBで、古い検証用targetやブログのプレビューが大きかった。&lt;/p&gt;
&lt;p&gt;こちらでは、比較用バイナリやログを残し、増分ビルドキャッシュ、再生成できるブログの出力、解析ツールの検証用ビルドを整理した。空きは約58GiBになり、実際に増えたのは約5GiBだった。ディレクトリの表示サイズと、ファイルシステム全体で回復した容量は同じ数字にならないので、回収量は整理後の空きで見るようにした。&lt;/p&gt;
&lt;p&gt;外付けSSDをDockerへ渡す案も考えた。しかし、起動済みコンテナのマウント構成を変えるには通常はコンテナの再作成が必要になる。Mac側のディレクトリを共有するだけでは、先ほどの共有ファイルシステムの問題が残る可能性もある。&lt;/p&gt;
&lt;p&gt;今は再起動できないので、そこは先に進めなかった。追加のストレージが欲しいという話と、今の作業を止めずに使えるという話は分けて考える必要があった。今日できる範囲では、保持すべきものを明確にしてキャッシュを整理する方が現実的だった。&lt;/p&gt;
&lt;h2 id="bootstrapのどこに時間がかかっているのか"&gt;bootstrapのどこに時間がかかっているのか&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/ieee0824/omoikane/issues/1506"&gt;iframeを80世代保持するテストの課題&lt;/a&gt;は、前日から続いている。GCの改善候補は得られていたが、それだけでMacの時間制限が解消したとは言えなかった。&lt;/p&gt;
&lt;p&gt;今日の静的監査では、大量に生成される関数やアクセサーの構造が詳しく分かってきた。しかし、生成される数が多いからといって、そのまま共有できるわけではない。公開される関数の同一性や、Realmごとの状態、HomeObjectなどの保持関係がある。&lt;/p&gt;
&lt;p&gt;小さいhelperをinline化する候補も見つかったが、それも安全な削減として確定したわけではない。引数の式にgetterや文字列変換が含まれれば、展開した結果、評価回数を増やすだけで挙動が変わり得る。候補の数から改善率を出すのは早かった。&lt;/p&gt;
&lt;p&gt;そこで、まず時間を分けて測る方針を選んだ。parse、ASTのclone、scope解析、link、bytecode生成、evaluateといった区間を記録し、どこを調べるべきか決める。大きな再設計に入る前に、一度内訳を見たかった。&lt;/p&gt;
&lt;p&gt;親のbootstrapが約276ms、iframe側が約291msだった。内訳は次のとおりだった。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;区間&lt;/th&gt;
&lt;th style="text-align: right"&gt;親&lt;/th&gt;
&lt;th style="text-align: right"&gt;iframe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bootstrap全体&lt;/td&gt;
&lt;td style="text-align: right"&gt;276.2ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;290.6ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;parse&lt;/td&gt;
&lt;td style="text-align: right"&gt;137.7ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;123.9ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AST clone&lt;/td&gt;
&lt;td style="text-align: right"&gt;19.8ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;20.0ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;scope解析&lt;/td&gt;
&lt;td style="text-align: right"&gt;21.2ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;21.4ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;link&lt;/td&gt;
&lt;td style="text-align: right"&gt;55.3ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;63.7ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;うちbytecode生成&lt;/td&gt;
&lt;td style="text-align: right"&gt;49.8ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;58.0ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;evaluate&lt;/td&gt;
&lt;td style="text-align: right"&gt;40.4ms&lt;/td&gt;
&lt;td style="text-align: right"&gt;60.5ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;bytecode生成はlinkに含まれるので、両方を足してはいけない。ほかの区間も包含関係を確認し、兄弟の区間が重ならないことを確かめたうえで集計している。&lt;/p&gt;
&lt;p&gt;条件も大事で、これはLinux上のtest profileで一回実行した値だ。GCの計測と時間記録そのものの負荷を含む。release buildやMacの絶対値ではなく、改善率を予測できる数字でもない。それでも、この実行ではparseが親の約50%、iframe側の約43%を占めていたことが分かった。&lt;/p&gt;
&lt;h2 id="キャッシュが両方missだった"&gt;キャッシュが両方missだった&lt;/h2&gt;
&lt;p&gt;今回観測したのは、親のbootstrap一回とiframeのbootstrap一回の、合わせて二つだ。どちらもsource cacheとparsed cacheがmissだった。二つのbootstrapでは、束縛名の組が異なっていた。&lt;/p&gt;
&lt;p&gt;ここを雑に読むと、「iframeを二回作ってもキャッシュが効かない」と言ってしまいそうになるが、その測定はしていない。warm状態の繰り返しを測った結果ではない。分かっているのは、今回の親とiframeの二つでmissしたということまでだ。&lt;/p&gt;
&lt;p&gt;キャッシュに当たらない理由が、必要な分離によるものなのか、共有できる部分があるのかは、別の調査になる。時間を測る前からimmutableなtemplateを再利用する案はあったが、compileの時間が大きいから即採用、というわけにはいかない。再利用後に構築し直す状態のコストもある。&lt;/p&gt;
&lt;p&gt;次は、キーの作られ方、束縛名の差の由来、キャッシュの所有範囲や寿命をコードから読む静的調査を承認した。追加の大きな計測や製品変更まで一括で許可したわけではない。まず、このmissが何を意味するのかを知りたい。&lt;/p&gt;</description></item><item><title>Omoikane開発日記：GCを12%軽くし、調査に使うnayutaも育てた日</title><link>https://blog.ast.moe/blog/2026-11-04/</link><pubDate>Wed, 04 Nov 2026 22:25:44 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-11-04/</guid><description>&lt;p&gt;今日もOmoikaneの開発状況をcc-sakuraで確認していた。新しい機能が一つ増えて終わり、という日ではなかった。GCの調査が少し進み、テストの足場にあった問題を直し、その調査に使う静的解析ツールも改良した。&lt;/p&gt;
&lt;p&gt;一番分かりやすい成果は、あるGC改善候補について、Linux上の80世代iframe保持テストが平均51.94秒から45.48秒になったことだ。同じ条件で修正前後を5組比較し、平均で12.44%短くなった。ただし、これでブラウザ全体が12%速くなったわけでも、MacのCIで続いている失敗が直ったわけでもない。その確認はまだ先に残っている。&lt;/p&gt;
&lt;p&gt;もう一つ面白かったのは、「このテスト、そもそも計算量が重くなっていないか」と聞いたところから、nayutaの改善と配布までつながったことだった。調査する対象だけでなく、調査に使う道具も同じ日に育っている。&lt;/p&gt;
&lt;p&gt;この記事は2026年11月4日22時22分ごろまでに確認した記録を基にしている。執筆中にも検証は動いているので、ここから先の結果は混ぜない。参加環境のハンドルネームは伏せ、実装担当、検証担当、調整担当、実機担当という役割で書く。cc-sakuraでの開発方法そのものは&lt;a href="https://blog.ast.moe/blog/2026-11-03/"&gt;昨日の記事&lt;/a&gt;にまとめたので、今日はその仕組みの中で何が起きていたかを残したい。&lt;/p&gt;
&lt;h2 id="動いている仕事と終わった仕事は違う"&gt;動いている仕事と、終わった仕事は違う&lt;/h2&gt;
&lt;p&gt;朝から晩までずっと同じ画面を見張っているわけではない。私は途中でroomを確認して、各自が何をしているのか、こちらの判断を待って止まっていないかを聞いている。AI同士で相談して進められる部分は、そのまま進めてもらう。&lt;/p&gt;
&lt;p&gt;今日の流れを追うと、実装担当が修正案を作り、別環境でテストを回し、独立した担当が結果を読み直すという往復が多かった。修正前に落ちたケースが、修正後には通る。同じソースを使ったか、同じテストを実行したか、ログの集計に漏れがないか。そうした確認がroomに積み上がっていく。&lt;/p&gt;
&lt;p&gt;これは安心材料になる一方、単に「テスト成功」と言われただけでは何が終わったのか分からなくなる。対象一件が通ったのか、unit全体が通ったのか、integrationも終わったのか。通常のfeatureなのか、JITの検証用featureを有効にしているのか。条件が違えば、同じPASSという単語でも意味は違う。&lt;/p&gt;
&lt;p&gt;そこで、今日の状況は三つに分けて見ることにした。変更そのものの妥当性、ローカル検証の到達点、CIを通してマージできる状態か。この三つは連動するが、同じものではない。良さそうな修正ができても、CIがディスク不足で止まれば最後の確認は残る。&lt;/p&gt;
&lt;p&gt;22時22分時点の未マージPRは23件あった。そのうち一件は保存用で、もともとマージ対象ではない。残りもすべてが同じ理由で止まっているわけではないが、MacのGate5とCIの容量問題が広く影響している。新しいPRが作られているので作業は進んでいる。それでも、mainに取り込まれた変更だけを見ると、進み方は遅く見える。&lt;/p&gt;
&lt;h2 id="80回のループなのになぜこんなに重いのか"&gt;80回のループなのに、なぜこんなに重いのか&lt;/h2&gt;
&lt;p&gt;長く追っているのが、&lt;a href="https://github.com/ieee0824/omoikane/issues/1506"&gt;80世代のiframeを保持するテストの失敗&lt;/a&gt;だ。このテストは、iframeの文書を更新しながら過去の文書への参照を残し、古い世代も正しく使えることを確かめる。参照を外した後には、不要になった文書が回収されることも確認する。&lt;/p&gt;
&lt;p&gt;大まかな形だけ書くと、次のようになる。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;80回繰り返す:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iframeの文書を更新する
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 更新した文書への参照を配列に残す
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GCを実行する
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;保持した80世代が読めることを確認する
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;参照を外す
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GCを実行する
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;古い文書やノードが回収されたことを確認する
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;テストの表面には、何重ものループが並んでいるわけではない。それぞれの更新は別のスクリプト実行になっている。それでも、一回の更新の中で、それまでに蓄積した文書や参照を全部見るなら、全体の仕事量は単純な80回では済まない。&lt;/p&gt;
&lt;p&gt;世代を一つ増やすたびに既存の世代を走査すると、仕事量は1、2、3と増える。合計すれば二乗に近づく。さらに別の走査が内側にあれば、見た目以上に重くなる。Rust側のループだけを見てO(N)と言っても、呼び出し先の処理を定数として扱ってよいかは別に調べる必要がある。&lt;/p&gt;
&lt;p&gt;そこで「テスト自体がO(n^m)みたいな実装になっていないか」と聞いた。テストが悪いと決めつけたいわけではない。製品の不具合を見つけるためのテストが、別の無駄な処理によってタイムアウトしているなら、そこも調査対象にしたかった。&lt;/p&gt;
&lt;p&gt;ここで重要なのは、80世代を保持すること自体には意味があることだ。参照が生きている古い文書を、都合のよい個数で捨ててはいけない。回数を減らして通すだけでは、検証したかった性質を弱める可能性がある。負荷を減らすなら、何を保証したいテストなのかを保ったまま、無駄な処理を取り除きたい。&lt;/p&gt;
&lt;p&gt;今の計測は80世代に固定した修正前後比較なので、計算量の次数まで確定したわけではない。10、20、40、80世代と変えて時間や走査回数を見れば、増え方はもっと分かる。ただ、まずは現在落ちている条件で、具体的な修正が効くかを確かめる段階にいる。&lt;/p&gt;
&lt;h2 id="gc改善はlinuxで5組とも同じ方向に動いた"&gt;GC改善は、Linuxで5組とも同じ方向に動いた&lt;/h2&gt;
&lt;p&gt;今回の候補は、minor GCで扱う参照の処理について、重複した投入を減らすものだ。古い世代を無条件に無視するような変更ではない。保持する必要がある参照は維持したまま、同じ仕事を重ねて行う部分を減らす。&lt;/p&gt;
&lt;p&gt;候補は&lt;a href="https://github.com/ieee0824/omoikane/commit/0344ada372fafc4d16830396c12fcbf3c9279d20"&gt;固定コミット0344ada3&lt;/a&gt;として保存した。基準となるmainとの差分はGC関連の6ファイルで、例外保持の別修正は含めていない。複数の改善を一緒に載せると、どの変更が時間差を作ったのか分からなくなるためだ。&lt;/p&gt;
&lt;p&gt;比較はAを修正前、Bを修正後として、AB、BA、AB、BA、ABの順に行った。毎回ビルドし直すとコンパイル時間やキャッシュ状態が測定に入り込むので、AとBのテストバイナリを保存し、それを直接実行している。通常feature、対象テスト一件、テストスレッド一つという条件をそろえた。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;組&lt;/th&gt;
&lt;th style="text-align: right"&gt;A：修正前&lt;/th&gt;
&lt;th style="text-align: right"&gt;B：修正後&lt;/th&gt;
&lt;th style="text-align: right"&gt;短縮時間&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style="text-align: right"&gt;51.79秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;45.41秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.38秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td style="text-align: right"&gt;51.88秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;45.53秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.35秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td style="text-align: right"&gt;52.25秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;45.45秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.80秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td style="text-align: right"&gt;51.93秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;45.54秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.39秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;51.84秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;45.45秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.39秒&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;表は実行順にかかわらずAとBの列をそろえている。10回の実行はすべて成功した。平均はAが51.938秒、Bが45.476秒で、差は6.462秒。平均に対して12.44%の短縮になる。担当の報告だけでなく、別の担当も元ログから集計し直して一致を確認した。&lt;/p&gt;
&lt;p&gt;この結果はかなり分かりやすい。5組すべてでBが短く、各条件のばらつきより差が大きい。ただし、このLinux環境、この単一テスト、この設定での観測だ。ブラウザ全体の応答速度や、別のCPUでの改善率を表しているわけではない。&lt;/p&gt;
&lt;p&gt;もう一つ紛らわしいのが、テスト全体の約52秒と、問題になっているJavaScript実行の5秒制限の関係だ。テストは複数のスクリプト実行やGCなどを含む。その合計時間が短くなっても、ある一回の実行が制限を超えなくなるとは限らない。今回は内部の5秒に対する余裕までは測れていない。&lt;/p&gt;
&lt;p&gt;だから「12%速くなったので解決」とはしなかった。次はMacで同じ候補を比較する。今日の時点で実機担当を再開し、共有ホストで重い処理が重ならないことを確認して、AとBのビルド準備に入った。Linuxで使ったバイナリを持っていくのではなく、Mac上でそれぞれビルドして比較する。&lt;/p&gt;
&lt;p&gt;最初の一組はAとBを両方実行する。Aで問題が再現しなければ、その一組で止めて「実機では元の失敗を再現できなかった」と記録する。元のRuntimeLimitが再現した場合は予定した5組を走らせる。ビルド失敗やSIGKILLを、元のテスト失敗と同じ扱いにしないことも事前に決めた。&lt;/p&gt;
&lt;h2 id="二行の修正でも確かめる範囲は広い"&gt;二行の修正でも、確かめる範囲は広い&lt;/h2&gt;
&lt;p&gt;GCの性能調査とは別に、例外の寿命に関する修正も進んだ。&lt;a href="https://github.com/ieee0824/omoikane/pull/1718"&gt;PR #1718&lt;/a&gt;は、iframeのスクリプト評価が例外を返した後、currentScriptを解除する間もその例外を保持する変更だ。&lt;/p&gt;
&lt;p&gt;後始末のためにJavaScriptを実行すると、その中でGCが起きることがある。Rustのローカル変数に例外が残っていても、その値がGC側から到達可能として認識されているとは限らない。これから呼び出し元へ返すつもりの例外が、返す前に回収されてしまう。&lt;/p&gt;
&lt;p&gt;修正の中心は、例外を一時的なRootedで保持する短いコードだった。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; _error_root &lt;span style="color:#f92672"&gt;=&lt;/span&gt; result.as_ref().err().cloned().map(Rooted::new);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;大事なのは行数ではなく、この変数が生きている区間だ。currentScriptの解除、realmの復元、返却値の構築まで保持し、関数を抜けた後には一時ルートを解放する。ずっと保持すれば回収されない問題に変わってしまうので、守るだけでなく手放すことも確かめる必要がある。&lt;/p&gt;</description></item></channel></rss>