70歳の挑戦... Arduino IDEのコンパイルオプション -Ofast でマンデルブロー集合描画高速化しました

　前回の「<a href="https://qiita.com/Mobu_Kyoto/items/afbad1deeb4ddd5b9b92">マンデルブロー集合の描画高速化</a>」から更なる高速化（20枚描画に49秒）が出来ました。
![8Para_49s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/def9f22c-8232-ff0c-8f21-d8747ae604de.jpeg)

　次の２点の効果が大きいです。
 
:::note
１．ピクセル情報保存用に、大サイズの配列 cels[480][320] が作成できたこと
２．オプション -Ofast でコンパイルできたこと
:::
　１は以前メモリー不足で不可能だったものが、今回使用している「ESP32-S3」のフラッシュメモリーサイズが 16MB であり可能のようです。
　２のオプションについては

    -O2 可
    -O3 不可
    -Ofast 可
という結果でした。私の環境ではこの設定は次のようにしました。Arduino IDE のフォルダー

    C:\Users\(ユーザー名)\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.2

内の platform.txt をもとに次のファイルを作成・編集し、同フォルダーに保存します。再度 Arduino IDEを立ち上げれば反映されます。
```html:platform.local.txt
---
# EXPERIMENTAL feature: optimization flags
#  - this is alpha and may be subject to change without notice
compiler.optimization_flags=-Ofast
compiler.optimization_flags.release=-Ofast
compiler.optimization_flags.debug=-Og -g3
---
```
　使用機材の構成は「<a href="https://qiita.com/Mobu_Kyoto/items/afbad1deeb4ddd5b9b92">マンデルブロー集合の描画高速化</a>」をご覧ください。
　Arduino IDE 2.3.2 での Tools は次のように設定しました。
![ESPS3_Tools.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/fb03a602-77d4-e741-e8a9-55ba4dbeedb3.jpeg)
LovyanGFXライブラリーで必要な myLovyanGFX.hpp です。スケッチと同じフォルダーに保存します。
```html:myLovyanGFX.hpp
#pragma once

#define LGFX_USE_V1

#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device 
{
  lgfx::Panel_ILI9488   _panel_instance;
  lgfx::Bus_Parallel8   _bus_instance;  // 8ビットパラレルバスのインスタンス (ESP32のみ)
  
public:
  LGFX(void) {   
    {                                   // バス制御の設定を行います。
      auto cfg = _bus_instance.config();// バス設定用の構造体を取得します。
      // 8ビットパラレルバスの設定
      cfg.freq_write = 20000000;  // 送信クロック (最大20MHz, 80MHzを整数で割った値に丸められます)
      cfg.pin_wr = 11;            // WR を接続しているピン番号
      cfg.pin_rd = 12; // must    // RD を接続しているピン番号
      cfg.pin_rs = 10;            // RS(D/C)を接続しているピン番号
      cfg.pin_d0 = 18;            // D0を接続しているピン番号
      cfg.pin_d1 = 37;            // D1を接続しているピン番号
      cfg.pin_d2 = 17;            // D2を接続しているピン番号
      cfg.pin_d3 = 38;            // D3を接続しているピン番号
      cfg.pin_d4 = 16;            // D4を接続しているピン番号
      cfg.pin_d5 = 39;            // D5を接続しているピン番号
      cfg.pin_d6 = 15;            // D6を接続しているピン番号
      cfg.pin_d7 = 40;            // D7を接続しているピン番号
      _bus_instance.config(cfg);               // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance);  // バスをパネルにセットします。
    }

    {                                       // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config();  // 表示パネル設定用の構造体を取得します。
      cfg.pin_cs  =  9;    // 27 CSが接続されているピン番号 (-1 = disable)
      cfg.pin_rst =  8;   // 33 RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy = -1;  // BUSYが接続されているピン番号 (-1 = disable)
      cfg.memory_width = 320;                // ドライバICがサポートしている最大の幅
      cfg.memory_height = 480;               // ドライバICがサポートしている最大の高さ
      cfg.panel_width = 320;     // 実際に表示可能な幅
      cfg.panel_height = 480;    // 実際に表示可能な高さ
      cfg.offset_x = 0;          // パネルのX方向オフセット量
      cfg.offset_y = 0;          // パネルのY方向オフセット量
      cfg.offset_rotation = 0;   // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel = 8;  // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits = 1;   // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable = true;       // データ読出しが可能な場合 trueに設定
      cfg.invert = false;        // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order = false;     // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit = false;    // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared = false;    // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)
      _panel_instance.config(cfg);
    }

    setPanel(&_panel_instance);  // 使用するパネルをセットします。
  }

};
```
バージョンアップしたスケッチ本体です。

```c:ESP_S3_Mandelbrot_8Para_2.ino
// Global variables 
//        use 174940 bytes (53%) of dynamic memory, 
//    leaving 152740 bytes for local variables.
// Maximum is 327680 bytes.
// this takes 49 seconds.

#include "myLovyanGFX.hpp"

static LGFX tft;                 // LGFXのインスタンスを作成。

const uint16_t col[]{
  0x0000,  //0
  0x2228,  //1
  0x867E,  //2LightSkyBlue
  0xFF1B,  //3MistyRose
  0x480F,  //4Indigo
  0xAEBC,  //5LightBlue
  0xF81F,  //6Magenta
  0xFFFB,  //7LightYellow
  0x07FF,  //8Cyan
  0xF800,  //9Red
  0x07E0,  //10Green
  0x001F,  //11Blue
  0xFCEE,  //12LightSalmon
  0xEF31,  //13Khaki
  0xDFFF,  //14LightCyan
  0xFFE0   //15Yellow
};

#define W 480// tft.width()
#define H 320// tft.heght()
#define X0 240//tft.width()/2
#define Y0 160//tft.height()/2

const float toRad = PI / 180.0;//度をラジアンに変換
const float absZ = 4.0;       // 発散判定
const uint16_t page = 20;
const uint16_t dp = 2;

// default Mandelbrot Set -----------------------------------------
// viewpW=2.4; CtoX = 0.0; CtoY = 0.0;
// repeat=200; page=1; dp=0;
// ----------------------------------------------------------------

// viewpW = 0.00024; // 10000倍に拡大
// Viewport width; Viewport Center CtoX;CtoY; 

// 風景の良い場所例
// example 1
// viewpW = 0.00024; CtoX = -1.24997; CtoY = -0.00877;
// repeat=190; page=28; dp=2;

// example 2
// viewpW = 0.00024; CtoX=-1.401;CtoY=-0.000178;
// repeat=300; page=19; dp=2;

const float viewpW = 0.0012;    // viewport width(window width)
const float di = viewpW / 480.0;// real/imaginary calculation step
                                // 1 pixel in Real and Imaginary number
const float CtoX = -1.19998;    // 現在の表示（viewpW）の中心
const float CtoY = -0.15507; 

uint16_t repeat = 120;  // 反復上限、page進行に応じ増加させる
uint16_t k;             // 反復回

float x, y, zr, zi, R, I;//複素計算変数
uint16_t C[800];        // 色配列

uint32_t now, starttime;

uint16_t pi;
int16_t j,i;
uint16_t Xi,Yj;
bool cels[W][H];  // 各セルの発散状況記憶

void setup() {
  Serial.begin(115200);
  Serial.println("begin");
  tft.init();
  tft.setRotation(1);

  for(uint16_t l=0;l<W;l++){
    for(uint16_t m=0;m<H;m++) cels[l][m]=false;
  }

  for(uint16_t n = 0; n < 800; n++) C[n] = ColorSel(n);

  starttime=millis();
  tft.startWrite(); // SPIでなくても高速化に効果ありかも
  for (pi = 1; pi <= page; pi++) {// 反復数上限を増やしていく
    Mandelbrot();

    now = millis();
    tft.setCursor(0,0);
    tft.printf("%2dpage", pi);
    tft.setCursor(0,8);
    tft.printf("%.0fs", (now - starttime)*0.001);

    repeat += dp * pi;
  }
  grid();
}

void Mandelbrot(void) {
  for ( j = - Y0; j < Y0; j++) {
    Yj = Y0-j-1;
    y = j * di + CtoY;
    for ( i = - X0; i < X0; i++) {
      Xi = X0 + i;

      if(cels[Xi][Yj]==false){ // 未発散のピクセル(黒)のみ計算
        x = i * di + CtoX;
        zr = 0.0;
        zi = 0.0;
        k = 0;//-1;
        do {
          k++;
          R = zr * zr - zi * zi + x;
          I = 2 * zr * zi + y;
          zr = R;
          zi = I;
          if ( k == repeat ) break;
        } while ( (zr * zr + zi * zi) < absZ );

        if (k < repeat) {// 新たに発散したピクセルのみ描画
          tft.drawPixel(Xi, Yj, C[k]);// 0x00F +0x0100*k+ 2*k);
          cels[Xi][Yj]=true;
        }

      }

    }
  }
}

void grid(void) {
  tft.drawLine(X0 + 100, 0, X0 + 100, tft.height(), 0xFFFF);
  tft.drawLine(X0 - 100, 0, X0 - 100, tft.height(), 0xFFFF);
  tft.drawLine(X0 + 200, 0, X0 + 200, tft.height(), 0xFFFF);
  tft.drawLine(X0 - 200, 0, X0 - 200, tft.height(), 0xFFFF);
  tft.drawLine( 0, Y0 + 100, tft.width(), Y0 + 100, 0xFFFF);
  tft.drawLine( 0, Y0 - 100, tft.width(), Y0 - 100, 0xFFFF);
  tft.drawLine(X0, 0, X0, tft.height(), 0xFFFF);  // 中心線
  tft.drawLine(0, Y0, tft.width(), Y0, 0xFFFF);
  tft.drawString("1.0",X0+192,H-8);
  tft.drawString("0",0,Y0-3);
  tft.drawString("0",X0-2,H-8);
  tft.drawString("-1.0",X0-213,H-8);
  tft.drawString("End",0,H-8);
}

uint16_t ColorSel(uint16_t c) {
  uint16_t re, gr, bl;     // color 0-255
  if (c < 16) return col[c];
  else {
    c -= 16;
    re = 128 + 126 * cos(c * toRad);
    gr = 128 + 126 * cos((c + 120) * toRad);
    bl = 128 + 126 * cos((c + 240) * toRad);
    return tft.color565(re, gr, bl);
  }
}

void loop(void) {
}
```
　最後まで見ていただきありがとうございました。