ESP32-Spinlockとmutexの違いについて検証してみた

ESP32-freeRTOSのtask関連ラッピングクラス2で作成したクリティカルセクションを示すためのSpinlockと同じようなインターフェースを持つものとしてstd::mutexがあるので、これらで動作がどう違うのかを見てみたいと思う。

概要

SpinlockはCritical Sections & Disabling Interruptsをもとに作成されたものでCPUコアの実行権を資源とみなし排他制御を行う機能で、std::mutexはhttps://cpprefjp.github.io/reference/mutex/mutex.htmlに示されているように「共有リソースを排他制御するためのクラス」になる。

std::mutex

ソースをたどっていくと最終的にはxSemaphoreCreateMutexを利用したものであることが分かった。

  • std::mutex
    packages\toolchain-xtensa32\xtensa-esp32-elf\include\c++\8.4.0\xtensa-esp32-elf\bits\gthr-default.h内でpthreadを使っていた
  • pthread関連
    packages\framework-espidf\components\pthread\pthread.cでxSemaphoreCreateMutex関連を使用していることが分かった。
  • xSemaphoreCreateMutex
    xQueueAltGenericReceiveまで行ったのだがそれ以降についてはちょっとわからずじまい。

Spinlock

Critical Sections & Disabling InterruptsではtaskENTER_CRITICALやportDISABLE_INTERRUPTSを使うように記載がされているが、この実装を追っていくと最終的にはspinlock_~系を使うようになっていた。

spinlock_系のソースは、packages\framework-espidf\components\esp_hw_support\include\soc\spinlock.hに記載がある。

実際の動作

2つのタスクで同一オブジェクトのロックを使ってみた場合

librarieseesp32/samples/mutexttest2.cpp at master · take4blue/librarieseesp32
Contribute to take4blue/librarieseesp32 development by creating an account on GitHub.

上のサンプルソースを使い、Spinlockとmutexの切り替えと、タスクを同一コア、異コアで分けた時の動きを追ってみた。
動きは、2つのタスクで方形波を出すのだがそのタイミング調整にets_delay_usを使用しこの命令を出している間ロックをかけるようにしてある。

  • Spinlock、同一コア

    一部いびつだが、それなりにそろった方形波になった。
    周期は4.1004~4.1005ms。若干オーバーヘッドがあるのか。
  • std::mutex、同一コア

    綺麗にそろった方形波になった。タイミングはets_delay_usを使用した間隔の2倍になっている。具体的には4.0004~4.0005msの周期。
  • Spinlock、異コア

    動きとして同一コアでの動作と異なるところはない。Spinlockをかけた部分で動作が止まり、コア0、コア1で交互に動作が開始されている。
    SpinlockのロックはFIFO的な動作をしていると感じることができる。
    周期的なものは4.0002msで同一コアでの動作よりは遅延がなくなってきているような感じ。
  • std::mutex、異コア

    動作としてはSpinlockと同じ感じのFIFO的なものを想像していたのだが、実際は異なった。優先順位があるのかそれともタイミング的な問題なのか。
    片方のコアのロックが外れないような状態になってしまった。

プログラムサイズ的なものを見てみると、Spinlockの方が若干小さくなるようだ。

割り込みがある場合の動作

librarieseesp32/samples/mutexttest1.cpp at master · take4blue/librarieseesp32
Contribute to take4blue/librarieseesp32 development by creating an account on GitHub.

こちらはタスクのループ中のets_delay_usでロックをかけ、それとは別にタイマー割り込みを行うようなプログラムにしている。
Spinlockはもともとの仕様としてロック中に割り込みがされないことは判明しているのだが、mutexはどうなのだろうかという好奇心で調べ始めたもの。

ロジアナの出力は上段がタスクでの方形波、下段が割り込みでの方形波になっている。

  • Spinlock

    これは基本想定通りの動作になった。
    タスクでロック中、割り込み禁止なので方形波が生成できないようになっている。
  • mutex

    こちらもある意味想定通りなのかも。
    mutexには割り込みを禁止するようなことが記述されていないので、上のタスクでロック中でも割り込み処理がきちんと行われていることが分かる。

最後に

どれを使用するか、結局その仕様通りの使い方をするしかないのだろう。

SpinlockはCPUコアの命令実行権を排他管理するために、なので同一コア内でのみ使用するように。

mutexは普通の共有資源の排他管理に。異タスク、異コア間で使うのが正しいのだろう。
ただmutexを別コアで使った場合のロック取得に関する動作が気になるところではあるのだが。

コメント

タイトルとURLをコピーしました