fiord Advent Calendar 2025 3 日目の内容となります。
digitalWrite って遅い?
割り込み処理を行う際に、digitalWrite() は遅いのでレジスタを直接操作するように、という内容のレビューを貰ったことがあります。
digitalWrite() の内容も基本的に特定のレジスタの書き換えのみだと思っていたので、そこまで遅いのか?という感覚があり、簡易的にですが、実際に測ってみることにしました。
検証環境/検証方法
- ボード: Arduino UNO R3
- 検証方法: 100,000 回の HIGH → LOW を繰り返し、その時間を
micros()で計測
コード
軽く調べてみたら、ライブラリがありました。
https://github.com/ArminJo/digitalWriteFast
digitalWriteFast で Arduino IDE 上で検索したらライブラリがあったため、これを導入した状態でのコードとなります。
#include <digitalWriteFast.h>
const unsigned long LOOPS = 100000UL; // 10万回繰り返し
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
Serial.println(F("Arduino UNO digitalWrite vs ポート直接操作 速度比較"));
Serial.println(F("測定回数: 100,000回 (HIGH→LOW)"));
Serial.println();
delay(2000);
}
void loop() {
// ───────────────────
// 1. digitalWrite 版
// ───────────────────
unsigned long start1 = micros();
for (unsigned long i = 0; i < LOOPS; i++) {
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LED_BUILTIN, LOW);
}
unsigned long end1 = micros();
unsigned long time1 = end1 - start1;
// ───────────────────
// 2. ポート直接操作版 (PORTB)
// ───────────────────
unsigned long start2 = micros();
for (unsigned long i = 0; i < LOOPS; i++) {
PORTB |= _BV(PB5); // HIGH
PORTB &= ~_BV(PB5); // LOW
}
unsigned long end2 = micros();
unsigned long time2 = end2 - start2;
// ───────────────────
// 3. 高速デジタルライブラリ版 (参考)
// FastDigitalやdigitalWriteFast2 など
// ───────────────────
unsigned long start3 = micros();
for (unsigned long i = 0; i < LOOPS; i++) {
digitalWriteFast(LED_BUILTIN, HIGH);
digitalWriteFast(LED_BUILTIN, LOW);
}
unsigned long end3 = micros();
unsigned long time3 = end3 - start3;
// ───────────────────
// 結果表示
// ───────────────────
Serial.println(F("=== 測定結果 ==="));
Serial.print(F("1. digitalWrite() : "));
Serial.print(time1); Serial.print(F(" us → "));
Serial.print((float)time1 * 1000 / LOOPS); Serial.println(F(" ns/回 (HIGH+LOW)"));
Serial.print(F("2. PORTB 直接操作 : "));
Serial.print(time2); Serial.print(F(" us → "));
Serial.print((float)time2 * 1000 / LOOPS); Serial.println(F(" ns/回 (HIGH+LOW)"));
Serial.print(F("3. digitalWriteFast() : "));
Serial.print(time3); Serial.print(F(" us → "));
Serial.print((float)time3 * 1000 / LOOPS); Serial.println(F(" ns/回 (HIGH+LOW)"));
delay(5000); // 5秒待機して次回測定
}
結果
5 回分の結果を記載します。
=== 測定結果 ===
1. digitalWrite() : 697892 us → 6978.92 ns/回 (HIGH+LOW)
2. PORTB 直接操作 : 62872 us → 628.72 ns/回 (HIGH+LOW)
3. digitalWriteFast() : 62880 us → 628.80 ns/回 (HIGH+LOW)
=== 測定結果 ===
1. digitalWrite() : 697892 us → 6978.92 ns/回 (HIGH+LOW)
2. PORTB 直接操作 : 62876 us → 628.76 ns/回 (HIGH+LOW)
3. digitalWriteFast() : 62876 us → 628.76 ns/回 (HIGH+LOW)
=== 測定結果 ===
1. digitalWrite() : 697888 us → 6978.88 ns/回 (HIGH+LOW)
2. PORTB 直接操作 : 62876 us → 628.76 ns/回 (HIGH+LOW)
3. digitalWriteFast() : 62876 us → 628.76 ns/回 (HIGH+LOW)
=== 測定結果 ===
1. digitalWrite() : 697896 us → 6978.96 ns/回 (HIGH+LOW)
2. PORTB 直接操作 : 62876 us → 628.76 ns/回 (HIGH+LOW)
3. digitalWriteFast() : 62872 us → 628.72 ns/回 (HIGH+LOW)
=== 測定結果 ===
1. digitalWrite() : 697888 us → 6978.88 ns/回 (HIGH+LOW)
2. PORTB 直接操作 : 62880 us → 628.80 ns/回 (HIGH+LOW)
3. digitalWriteFast() : 62876 us → 628.76 ns/回 (HIGH+LOW)
厳密には for ループのオーバーヘッドが存在するため、「何倍速い」と計測するにはこの検証は適切ではありません。ただし、少なくとも下記は言ってもよいかと思われます。
- レジスタを直接操作すると
digitalWriteと比べて非常に高速に動作させることが出来る。 -
digitalWriteには数マイクロ秒の時間がかかる
解説
この差ですが、digitalWrite はピン番号から lookup テーブルを参照します。「実際に書き込んでも問題無いか」という検証が事前に行われており、その結果として遅くなっているようです。
具体的には直接ソースコードを見るのが一番早いと思います。
https://github.com/arduino/ArduinoCore-avr/blob/6d0078318c0430b2f04a8c150d894f008ea0d317/cores/arduino/wiring_digital.c#L138
これ出されて「レジスタを直接弄った方が速い!」と言われたら、結構説得力があると思います。
まとめ
レジスタを直接操作すると高速に digitalWrite() を利用することが出来ます。ただ、「絶対に利用すべき」という訳でも無く、可読性が落ちてしまいますので、パフォーマンスとして digitalWrite() がボトルネックになる、数マイクロ秒でも惜しいようなリアルタイム処理を行うようなケースでのみ利用することが有効かと思います。
例えばですが、人間がボタンを押した際に割り込みを起動するようなケースで、digitalWrite() からレジスタを直接操作するようにしました!としても、その意味がほぼ無いかと思われます。