チカラの技術

電子工作やプログラミング

高速処理の為のプログラミング

秋月300円液晶の制御は大量のデータを扱います。
動画の表示では一秒間で3MB以上のデータ転送が必要になったりします。
 
本を読んだりプログラミングをしている内に、高速に処理する為の工夫が2つ程
見つかったので備忘録として記録します。
 
①大量に同じ処理をする場合にfor文やwhile文の分岐を減らす
②単純なメモリ間のデータ転送はint型(STM32なら32bit)にポインタをキャストして転送する。
 


①大量に同じ処理をする場合にfor文やwhile文の分岐を減らす
 配列memory からpeliphへデータをコピーする場合を考えてみます。
 
 とりあえず、普通のプログラム。
#define DISPLAY_WIDTH 400
#define BUFFER_LINE_SIZE 28
int i = 0;
for(i = 0 ; i  < (DISPLAY_WIDTH  * BUFFER_LINE_SIZE) ; i++)
{
       memory[i] = peliph[i];
 
うん、スッキリしているね。
でもね・・・遅いんだよぉぉぉ!!
1行処理する度に、(コピーに寄与しない)分岐命令が入るため、遅延が凄いんです。
分岐命令自体の処理サイクル遅延はもちろんですが、
STM32のフラッシュ読み込みは2サイクル(72MHz)必要で、それを解消するために
プリフェッチバッファがあるのですが、分岐命令のせいで
プリフェッチされた命令がキャンセルされたりします。
 
つまり、処理速度と命令読み出し速度の2点から分岐命令はイクナイ!のです。 
そこで下のように書き直します。
 
  高速化されたプログラム
#define DISPLAY_WIDTH 400
#define BUFFER_LINE_SIZE 28
int i = 0;
int k = 0;
 
/* DISPLAY_WIDTHの回数分命令を繰り返してfor分岐を無くす */
for(i =0 ; i < BUFFER_LINE_SIZE ; i++)
{
       memory[k] = peliph[k];
       k++;
       memory[k] = peliph[k];
       k++;
       memory[k] = peliph[k];
       k++;
       .
       .
       .
       ×400回くらい
 
DISPLAY_WIDTH の分、繰り返し命令ではなく
ソースコードを繰り返す事で分岐遅延を無くしています。
ソースコードなげぇぇぇ!!ってなりますが
処理速度は数倍速くなります。
ROMもふんだんに使いますが、STBeeは512kB。楽勝だね!


 
②単純なメモリ間のデータ転送はint型(STM32なら32bit)にポインタをキャストして転送する。
8bit変数の配列から8bit変数の配列へのコピーを考えます。
pic_dataの501個目を
mov_dataの201個目へとコピーしていきます
 
  普通のプログラム
byte pic_data[1000];
byte mov_data[700];
byte *pic_ptr;
byte *move_ptr;
 
pic_ptr =  &pic_data[500];
move_ptr =  &move_data[200];
 
int i = 0;
 
for(i =0 ; i <500 ; i++)
{  
       *move_ptr  = *pic_ptr ;
       pic_ptr++ ;
       move_ptr++ ;
}
 
普通にプログラミングしてたらこんな感じになると思います。 
これも早く処理する事ができます。
STM32は32bitマイコンですので32bit単位での転送が
CPU的に自然で処理が早いのです。
よってポインタを32bitのint型でキャストします。
 
  高速化されたプログラム
byte pic_data[1000];
byte mov_data[700];
int *pic_ptr;
int *move_ptr;
 
pic_ptr =  (int *)&pic_data[500];
move_ptr =  (int *)&move_data[200];
 
int i = 0;
 
/* 32bit長で転送するので繰り返し数を4分の1に。 */
for(i =0 ; i <125 ; i++)
{  
       *move_ptr  = *pic_ptr ;
       pic_ptr++ ;
       move_ptr++ ;
}
 
これで4倍速です。
byte型の変数配列だからってbyte単位で転送しなくても良いのですね。
高速処理したいけれど、配列数が32bitの倍数ではないという場合は
32bitの倍数へとnullを1~3byte突っ込んででも
配列を拡張すればよいと思います。
 
また、余談ですが今回のプログラムの i はuint16_t型でも利用できますが
32bitマイコンではわざわざ16bit→32bitへ暗黙的変換が行われ
処理が遅延する場合があるので、int型にしておく方が処理速度的に安心です。
 
この方法は多分、組み込みプログラミングをされている方なら
常識なのでしょうが、私は知らなかったので備忘録として記載しておきます。
 


 
以上です。
プログラミングとしての美しさは損なわれ、可読性も落ちてしまいますが
それでも限られたCPUパワーの中で高速に処理する為には
必要であると感じています。
 
ここが組み込みとPCプログラミングの違いなんでしょうね。