コンテンツにスキップ
これは開発版のドキュメントである. 内容は次のリリースまでに変更される可能性がある. 最新のリリース版は 0.6.x を参照.

パフォーマンス Tips

高頻度に焦点を更新するなど, スループットが要求される場面で性能を引き出すための指針.

複合コマンドと低レベルコマンド

Section titled “複合コマンドと低レベルコマンド”

Pattern はバッファ書き込み・再生設定・バンク切替をまとめて行う複合コマンドだが, これらは 1 フレームに融合して送られる. そのためフレーム数は低レベルコマンド 1 つと同じであり, 単に焦点を更新するだけなら Pattern をそのまま高頻度で送ってよい.

ただし, バンクを設定してしまえば, 毎フレームの焦点更新で必要なのはバッファの書き換え, すなわち WritePatternBuffer だけである. WritePatternBuffer のみを送ると, 毎フレームの再生設定とバンク切替を省けるため, デバイス側の 1 フレームあたりの処理が軽くなる.

最初に一度だけバンクを設定しておく. (最初はPatternを使っても良い.)

let mut builder = client.datagram_builder();
builder
.push(SetSilencer::disable())
.push(WritePatternBuffer {
bank: PatternBank::B0,
index: 0,
emissions: &patterns,
})
.push(ConfigPattern {
bank: PatternBank::B0,
config: SamplingConfig::FREQ_40K,
size: 1,
loop_behavior: LoopBehavior::Infinite,
});
for frame in &builder.build()? {
client.send_checked(frame).await?;
}

以降のホットループでは WritePatternBuffer のみを送る.

let mut buf = Frames::default();
let mut pending: VecDeque<ResponseFuture> = VecDeque::with_capacity(MAX_INFLIGHT);
for i in 0..NUM_POINTS {
let theta = 2.0 * PI * i as f32 / NUM_POINTS as f32;
let target = center
+ offset(
RADIUS_MM * theta.cos() * mm,
RADIUS_MM * theta.sin() * mm,
0.0 * mm,
);
autd3_rs_pattern::focus(
&geometry,
target,
wavelength,
&autd3_rs_pattern::FocusOption::default(),
&mut patterns,
);
let mut builder = client.datagram_builder();
builder.push(WritePatternBuffer {
bank: PatternBank::B0,
index: 0,
emissions: &patterns,
});
builder.build_into(&mut buf)?;
for frame in &buf {
if pending.len() >= MAX_INFLIGHT {
pending.pop_front().expect("non-empty").await?.check()?;
}
pending.push_back(client.send(frame).await?);
}
}
while let Some(fut) = pending.pop_front() {
fut.await?.check()?;
}

上のホットループでは, 応答を待たずに次々と送るstreamingを使っている. send でフレームをキューに積み, MAX_INFLIGHT を超えたら古い応答から回収する. 往復レイテンシが隠蔽されてスループットがさらに向上するため, 高頻度更新では stop-and-wait より streaming を選ぶ.

毎フレームの確保を避けるため, バッファは使い回す.

  • pattern_buffer() で得たバッファは, Pattern 計算関数の出力先として毎フレーム上書きできる.
  • Rust では Framesbuild_into で同じバッファへ書き出せば, build のたびに確保せずに済む. Python/C# では build_into は提供されない.

EtherCAT のリアルタイム性は OS の設定にも左右される. スレッドの優先度・CPU アフィニティ・タイマ精度などを調整することで, ジッタを抑えてより安定した高レート送信ができる可能性がある.

これらは ClientConfigrt_priority / rt_policy / rt_affinity, および Link Option の sync0_period / sync0_shift として設定できる. 適切な値は環境によって異なるため, 実測して決めるのがよい.

Windows では NIC のドライバを最新のものにしたうえで, 以下のとおりに設定することを推奨する.

デバイスマネージャーのアダプタのプロパティ → 詳細設定で

  • 割り込み調節 (Interrupt Moderation) → 無効
  • 割り込み調節率 (Interrupt Throttle Rate) → 無効
  • DMA Coalescing → 無効
  • 省電力イーサネット (Energy Efficient Ethernet) → 無効
  • フロー制御 (Flow Control) → 無効

リポジトリには, 性能の計測とチューニングのためのツールが用意されている.

  • cargo xtask tool perftest: スループットとレイテンシを計測する
  • cargo xtask tool synctune: 同期の安定性を計測し最適なパラメータを探索する

詳しい使い方は各ツールの --help を参照.