2021年8月2日月曜日

STM32CubeIDE Nucleo-G431KBの実行速度を測定 FPU

Cortex-M4は単精度FPUを備えています。STM32F767ZIはCortex-M7で倍精度も使えます。G431KBとF303K8、F446RE、F767ZIについてsinf()、cosf()、expf()の実行速度を測定しました。

STM32CubeIDEのバージョンは1.7.0です。

ソースコード(Core/Src/main.cに追加)


/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */

<中略>

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PI_F  (3.1415926f)
#define DIV_N (1000)
#define DELTA (2.0f*PI_F/DIV_N)
/* USER CODE END PD */

<中略>

/* USER CODE BEGIN PV */
float v[DIV_N] = {0};
/* USER CODE END PV */

<中略>

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
/* USER CODE END 0 */

<中略>

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  volatile float phi = 0.0f;
	  HAL_GPIO_WritePin(PIN_CHK1_GPIO_Port, PIN_CHK1_Pin, GPIO_PIN_SET);
	  for (int i = 0; i < DIV_N; i++) {
		  // Uncomment out the functions to be used.
		  v[i] = sinf(phi);
		  //v[i] = cosf(phi);
		  //v[i] = expf(phi);

		  phi += DELTA;
	  }
	  HAL_GPIO_WritePin(PIN_CHK1_GPIO_Port, PIN_CHK1_Pin, GPIO_PIN_RESET);
	  HAL_Delay(1);

	  //for (int i = 0; i < DIV_N; i++)  printf("%f\r\n", v[i]);
  }
  /* USER CODE END 3 */
}

メインループ内で算術関数をDIV_N=1000回繰り返し、その前後でGPIOをHigh/Low出力しオシロで間隔を測定しました。算術関数の切り替えは原始的にコメントアウトをつけたり外したりしてBuildし直して行っています。

算術関数がちゃんと使えているかどうか、結果が入ったv[]をUART出力(printf())しています。UART出力はかなり時間がかかるので、測定時にはprintf()コメントアウトしました。

STM32CubeIDEでのprintf()については「STM32CubeIDE: UARTでprintfを使う(浮動小数点数型あり)」で書きました。

各Nucleoボードの設定


Clock:
G431KB HSI→PLL 170MHz
F303K8 HSI→PLL  64MHz
F446RE HSE→PLL 180MHz
F767ZI HSE→PLL 216MHz

Pinout & Configuration:
System Core
  GPIO
    <Arduino D2に割り当てられるPin>
        GPIO mode: Output Push Pull
        Maximum ouput speed: Very High (F303K8はHigh)
        User Label: PIN_CHK1

Arduino D2に割り当てられるPinは、F303K8とG431KBはPA12、F446REはPA10、F767ZIはPF15です。

Build Configurations: Release 

Build Configurationsについてですが、DebugとReleaseを切り替えて実行するには、メニューの[Run] - [Run As...] - [1 STM32 Cortex-M C/C++ Application]からDebugかReleaseを選んで実行する必要があります。

最適化: -Ofast

Release Configurationのデフォルトは-Os (サイズ優先)ですが-Ofast (スピード優先)に変更しました。

G431KBの実行結果


sinf()

cosf()

expf()

UART出力


UART出力したデータをRlogin(ターミナルソフト)で受信しグラフ化しました。

sinf()

expf()

実行結果比較


関数名 機種名 周期(ms) パルス幅+(ms) パルス-(ms)
sinf() G431KB 3.005 1.04 1.96
sinf() F303K8 6.01 4.62 1.4
sinf() F446RE 2 1 1
sinf() F767ZI 3 1.6 1.4
cosf() G431KB 3 1.04 1.96
cosf() F303K8 6.01 4.54 1.48
cosf() F446RE 3 1.12 1.88
cosf() F767ZI 3 1.5 1.5
expf() G431KB 2 1 1
expf() F303K8 6.01 4.24 1.76
expf() F446RE 2 0.96 1.04
expf() F767ZI 3 1.28 1.72

周期がほぼ1ms区切りになっていますが、HAL_Delay()を使っているためだと思います。1ms単位のタイマーなので、1ms区切りでHAL_Delay()から抜ける動作をするようです。したがって周期ではなく、出力がHighになっている時間で比較します。


G431GBとF446REが同程度で最も速く、ついでF767ZI、F303K8が最も遅い結果になりました。GPIOの場合ほどではないですが、最も高スペックのはずのF767ZIの成績があまり芳しくないですね。

他の開発環境

以前、mbed OS5とSW4STM32で浮動小数点演算の実行時間を測定しました。


同じF446REの1000回ループで

関数 CubeIDE(us) mbed-cli(us) SW4STM32(us)
sinf() 1000 569 525
cosf() 1120 621 591
expf() 960 958 998

と、sinf()、cosf()の結果がかなり悪くなっています。Build時の設定が何かおかしいのかもしれませんね。

0 件のコメント:

コメントを投稿