fullseye

GPU 第 4 便: 光路・波長・偏光の一括計算(2026-11-02)

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。

1. 既存 CPU 実装との一致(float64)

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 本。

門の目安 1e-9 mm を超えた処方: 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 以内(テストの門)。

2. 時間

仕事 処方 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 回だけ小さく呼ぶ場合は負ける

近軸 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 段ある。

値の側の確認: 実演 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 まで。

3. 偏光(2448 x 2048、float64)

仕事 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 の履歴を保つので、より正しいのは前者。既存の定義は変えていない。

4. float32 の誤差(float64 との差)

処方 光線位置の最大差 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)。

5. 扱わないもの