lens_gpu.py(近軸・実光線・分光・公差 MC)と polar_gpu.py(偏光光線追跡・Jones / Mueller・偏光画像)の測定記録。
すべて tools/bench_gpu_optics.py の出力で、数字は docs/perf/gpu_optics_2026-11-02.json にある。
時間は転送(CPU → GPU → CPU)込み。best は 2 回目以降の最良、first は最初の 1 回(起動込み)。
float64 が既定。外挿した数字はこの文書に無い。打ち切ったものは打ち切ったと書いた。
測定中は別のエージェントが同じ機械で CPU と GPU を使っていた。CPU の時間には数十 % の揺れがある(同じ triplet の MC 1 万案が、自分の別の処理と重ねなかった回で 42.6 s、重なった回で 73.6 s)。
★最初の CPU 一括の測定では triplet の 2501 波長が 16〜19 s と出たが、regen_all と重なっていた。分解して測ると 0.8 s で、表は自分の重い処理を止めて測り直した値にした。倍率の桁は読めるが、有効数字 2 桁目は信用しないこと。
GPU: NVIDIA GeForce RTX 5090(float64)。実行中の最大メモリ 20.2 GB。
13 処方(単レンズ、貼り合わせ 2 種、Descartes 非球面、多項式非球面、奇数非球面、有限物体、面ごとの偏心・傾き、曲がった像面、放物面鏡、球面鏡、2 枚鏡、Cooke triplet)。実光線は 217 本、波長は C 線。近軸は F/d/C の 3 波長で 14 量すべて。
| 処方 | 近軸 相対差の最大 | valid 不一致 / 有効光線 | 像面の位置差 mm | 光路長差 mm | スポット RMS 差 mm |
|---|---|---|---|---|---|
| asphere | 0.0e+00 | 0 / 217 | 7.1e-15 | 4.3e-14 | 1.1e-15 |
| asphere_poly | 0.0e+00 | 0 / 192 | 7.1e-15 | 1.4e-14 | 1.8e-15 |
| catalog_doublet | 0.0e+00 | 0 / 192 | 1.6e-14 | 4.3e-14 | 1.6e-15 |
| curved_image | 0.0e+00 | 0 / 192 | 4.3e-14 | 5.7e-14 | 9.7e-16 |
| doublet | 0.0e+00 | 0 / 192 | 1.4e-14 | 4.3e-14 | 1.6e-15 |
| finite_object | 0.0e+00 | 0 / 169 | 1.8e-14 | 1.1e-13 | 1.0e-15 |
| odd_asphere | 0.0e+00 | 0 / 192 | 1.1e-14 | 4.3e-14 | 2.2e-15 |
| paraboloid | 0.0e+00 | 0 / 192 | 1.2e-10 | 1.1e-09 | 3.5e-13 |
| perturbed_surface | 0.0e+00 | 0 / 169 | 1.4e-14 | 4.3e-14 | 1.3e-16 |
| singlet | 0.0e+00 | 0 / 192 | 8.9e-15 | 2.8e-14 | 2.8e-17 |
| sphere_mirror | 0.0e+00 | 0 / 192 | 1.1e-14 | 7.1e-14 | 1.2e-15 |
| triplet | 0.0e+00 | 0 / 205 | 2.1e-14 | 4.3e-14 | 6.6e-16 |
| two_mirror | 0.0e+00 | 0 / 192 | 1.7e-08 | 4.6e-08 | 7.6e-10 |
公差 MC は乱数列まで揃え、1 案ずつ突き合わせた(CPU のループと同じ 1000 案)。
| 処方 | 案 | EFL 相対差の最大 | RMS 差の最大 mm | 失敗 |
|---|---|---|---|---|
| catalog_doublet | 1000 | 0.0e+00 | 5.7e-15 | 0 |
| triplet | 1000 | 9.8e-16 | 3.8e-15 | 0 |
近軸の差が厳密に 0.0 なのは、同じ式を同じ順に計算しているからで、門が見えていないからではない(test_gate_detects_a_wrong_lens で曲率を 1e-9 崩すと落ちる)。
GPU 上でも同じ表を取った: 放物面系を除く位置差の最大 4.3e-14 mm、valid の不一致 0 本。
two_mirror(1.7e-08 mm)、paraboloid は超えないが他より 4 桁大きい(1.2e-10 mm)原因は raytrace._intersect_conic の丸めで、lens_gpu の誤差ではない。放物面(k = -1)の軸の近くで 2 次方程式の A が 7.6e-9 まで小さくなり、(-B ∓ √D)/(2A) の小さい方の根が桁落ちする。60 桁の計算と比べると、CPU の交点は 7.2e-9 mm ずれていた。lens_gpu は安定な形 C/q で解くので 1e-11 mm 以内に入る(test_conic_root_beats_cpu_on_a_paraboloid)。CPU 側は値が変わるので今回は直していない。
閉形式の検算: 単一屈折面、厚肉レンズ(thick_lens の 5 量)、鏡 f = -R/2、放物面鏡と Descartes 非球面の軸上無収差(1e-9 mm 以内)、平面の Snell、平行平板の分光透過 (1 - R)² と内部透過 exp(-αt)、単一面の Fresnel の s / p、ブルースター角で T_p = 1、Malus、光線ごとの Mueller 行列の mueller_checks(physical / pure / passive)。
lensimage.through_focus_mtf(焦点 1 位置)とは絶対差 1e-8 以内で一致。optics の素子、stokes_analyze、polarization_demosaic、photoelastic_polarization_images、sfp_render、dolp_to_zenith も同じ入力で 1e-10 以内(テストの門)。
| 仕事 | 処方 | CPU 既存 | torch CPU | GPU | torch CPU / 既存 | GPU / 既存 | GPU / torch CPU |
|---|---|---|---|---|---|---|---|
| 近軸 1 回 | catalog_doublet | 41.1 µs | 765 µs | 2.76 ms | 負け 18.6 倍 | 負け 67.2 倍 | 負け 3.6 倍 |
| 光線 127 本 1 回 | catalog_doublet | 358 µs | 2.93 ms | 12.3 ms | 負け 8.2 倍 | 負け 34.5 倍 | 負け 4.2 倍 |
| 色収差 3 波長 | catalog_doublet | 1.85 ms | 3.21 ms | 13.5 ms | 負け 1.7 倍 | 負け 7.3 倍 | 負け 4.2 倍 |
| 色収差 301 波長 | catalog_doublet | 173 ms | 18.8 ms | 13.6 ms | 9.2 倍 | 12.8 倍 | 1.4 倍 |
| 公差 MC 1 万案(127 本) | catalog_doublet | 16.9 s | 590 ms | 28 ms | 28.6 倍 | 602.7 倍 | 21.0 倍 |
| 実演 1: 公差 MC 1 万案 × 31 波長 × 469 本 | catalog_doublet | 90 s で打ち切り(1470 / 10000 案) | 50.5 s | 1.46 s | 比較不能 | 比較不能 | 34.6 倍 |
| 実演 2: 2501 波長 × 817 本 | catalog_doublet | 2.89 s | 446 ms | 15.4 ms | 6.5 倍 | 187.5 倍 | 28.9 倍 |
| MTF 31 波長、瞳 128²(別名 0 / 31) | catalog_doublet | 752 ms | 109 ms | 18 ms | 6.9 倍 | 41.8 倍 | 6.1 倍 |
| 近軸 1 回 | triplet | 43.1 µs | 996 µs | 3.72 ms | 負け 23.1 倍 | 負け 86.2 倍 | 負け 3.7 倍 |
| 光線 127 本 1 回 | triplet | 518 µs | 7.89 ms | 50.9 ms | 負け 15.2 倍 | 負け 98.3 倍 | 負け 6.5 倍 |
| 色収差 3 波長 | triplet | 11.6 ms | 8.42 ms | 46.9 ms | 1.4 倍 | 負け 4.0 倍 | 負け 5.6 倍 |
| 色収差 301 波長 | triplet | 1.17 s | 39.2 ms | 48.6 ms | 29.8 倍 | 24.0 倍 | 負け 1.2 倍 |
| 公差 MC 1 万案(127 本) | triplet | 42.9 s | 1.07 s | 86.2 ms | 40.2 倍 | 497.5 倍 | 12.4 倍 |
| 実演 1: 公差 MC 1 万案 × 31 波長 × 469 本 | triplet | 90 s で打ち切り(619 / 10000 案) | 95.2 s | 3.23 s | 比較不能 | 比較不能 | 29.5 倍 |
| 実演 2: 2501 波長 × 817 本 | triplet | 12.2 s | 893 ms | 50.1 ms | 13.7 倍 | 243.7 倍 | 17.8 倍 |
| MTF 31 波長、瞳 128²(別名 0 / 31) | triplet | 1.05 s | 201 ms | 54.8 ms | 5.2 倍 | 19.1 倍 | 3.7 倍 |
最初の 1 回(GPU の起動込み)は別に記録した: 例えば triplet の 127 本 1 回は first 48.7 ms、best 50.9 ms。
実演 1 の CPU は 90 s で打ち切った(外挿しない)。
近軸 1 回・光線 127 本 1 回は、torch の方が既存 numpy より遅く、GPU はさらに遅い。テンソルの組み立てと演算ごとの間接費が仕事より大きい。device="auto" は仕事量 1,000,000(設計案 × 波長 × 面 × 127 本)未満なら CPU を選ぶ。小さい呼び出しは今までどおり raytrace を使うのが正しい。閾値の根拠: 301 波長 × 127 本 × 6 面(約 23 万)は torch CPU が勝ち、1 万案 × 127 本 × 6 面(約 760 万)は GPU が 12 倍勝つ。
公差 MC 1 万案の GPU / 既存が 603 倍と大きい。単純な GPU の演算速度の差ではなく、内訳は 2 段ある。
lens_system の検証、辞書の組み立て、paraxial_trace を Python で回す(1 案 1.69 ms)。この間接費が大半で、同じ torch を CPU で動かすだけで 29 倍縮む。値の側の確認: 実演 1 の rms_spot の平均は GPU 0.03860514369689、torch CPU 0.03860514369689 で 13 桁一致した。計算を省いた結果ではない。時間は .cpu() で結果を手元に戻すまでで、synchronize も入っている。
GPU の実行中メモリの最大は 20.2 GB(float64)。CARLA と同時に動かすなら tolerance_monte_carlo(chunk=...) で小さくする。既定の分割は空きメモリの 1/2 まで。
| 仕事 | numpy / scipy | torch CPU | GPU | CPU / 既存 | GPU / 既存 | 判定 |
|---|---|---|---|---|---|---|
| demosaic 8 枚 | 2.34 s | 1.67 s | 424 ms | 1.4 倍 | 5.5 倍 | 採用 |
| 光弾性 4 像 8 枚 | 15.4 s | 471 ms | 383 ms | 32.7 倍 | 40.1 倍 | 採用 |
| sfp_render 8 枚 | 11.9 s | 1.62 s | 734 ms | 7.3 倍 | 16.2 倍 | 採用 |
| dolp_to_zenith 1 枚 | 32.1 s | 5.29 s | 168 ms | 6.1 倍 | 191.4 倍 | 採用 |
| Stokes 解析 1 枚(numpy 場と比較) | 677 ms | 131 ms | 42.9 ms | 5.2 倍 | 15.8 倍 | 採用 |
| Mueller 3 素子 × 5 Mpix | 2.29 s | 295 ms | 105 ms | 7.8 倍 | 21.9 倍 | 採用 |
stokes_analyze のスカラー API を Python のループで全画素に回すと 31 s(5,013,504 画素、完走)。上の表の基準はこれでなく、同じ式を numpy の場で書いたもの。そちらの方が公平な比較になる。
optics.mueller_element の配列入力は MAX_FIELD_ELEMENTS(2^24 要素)で 5 Mpix の場を断る。表の numpy の値は、同じ式で上限の無い photoelastic._retarder_field で測った。
光線ごとの偏光(triplet、1000 案 × 31 波長 × 127 本): torch CPU 4.01 s、GPU 174 ms。既存の CPU 経路は無い(この計算自体が新規)。1 案 × 1 波長 × 127 本は torch CPU 9.51 ms、GPU 62.2 ms。
float32 の GPU: demosaic 8 枚 87.5 ms、Stokes 解析 26.3 ms。
| 仕事 | numpy | torch CPU | GPU |
|---|---|---|---|
| demosaic 256² 1 枚 | 2.13 ms | 810 µs | 725 µs |
| Stokes 解析 256² | (比較なし) | 1.73 ms | 791 µs |
| 屈折率 8 硝種 × 301 波長 | 19.2 ms(スカラーのループ) | 262 µs(numpy の一括、CPU テンソルに載せるまで) | 347 µs |
不採用: 屈折率の表は GPU で計算しない。W × 硝種の小さな表を 1 回作るだけなので、numpy の配列計算で足りる(index_table は numpy で計算して結果を載せるだけ)。
実寸の 6 項目は torch CPU でも既存より遅くならなかった。
多面の光線では、偏光を追った無偏光透過 M00 と、面ごとに無偏光の式を掛ける lensrender の値は 2 次の差で一致しない(貼り合わせで最大 2e-3 以内を門にした)。前者は s / p の履歴を保つので、より正しいのは前者。既存の定義は変えていない。
| 処方 | 光線位置の最大差 mm | EFL 相対差 | valid の差 | MC の RMS 相対差の最大 |
|---|---|---|---|---|
| catalog_doublet | 7.46e-06 | 2.46e-07 | 0 | 1.53e-04 |
| triplet | 1.29e-05 | 2.21e-07 | 0 | 1.98e-04 |
MC の RMS はスポット半径の小さい案で丸めに負ける。公差の分布を読むなら float64 を使う。demosaic の float32 の差は最大 9.1e-08(値域 1)。
lens_gpu は理想レンズ面 f、zernike_sag、afocal、ケラレ係数、aim="stop" を ValueError で断る。物体側テレセンは failed の印で返す。materials に k がある媒質だけ効く。同梱の Sellmeier 20 種と (nd, vd) は吸収 0 になる。コートと多重反射は入っていない。lensopt / chromconf / lensglobal の呼び出し元はまだ raytrace のままで、この一括経路へ繋いでいない。tests/test_public_reachability.py の不可視関数の総数を 948 → 977 に上げた。