Arduino:計算処理時間 @100円工作ラボ+(プラス)

メニュー0が利用出来ません メニュー2が利用出来ません

最近、あるCPUを使用して計算処理(主にlong型の除算)が意外なほど長くかかっていることに気付きarduinoでも試してみようと思いました

※ 入門編としてまだ先まで考えていましたが放置し過ぎて何をやろうとしていたのか忘れたので入門編終了です

・結果要約
結果から言うとarduinoでも除算はlong型よりもfloat型の方が速いですが僅差です
もしかすると元々float除算しか処理を用意しておらず、long除算の場合は一旦floatに置き換えて除算ということをコンパイラが自動的に行っているのかもしれません

ちなみに試すきっかけとなったCPUではlong除算はfloat除算の30倍近い時間がかかっていました
そのため除算の前にlongをfloat化して除算を行い、除算後にfloatをlong化することで計算を速くすることが出来ました
・測定方法
時間を測りたい処理の前後でIOポートを変化させてそれをシンクロスコープやロジックアナライザーで確認するというのがファームウェア開発を行っている方の常識かと思いますが、我が家にそんな物は有りません

嘘をついてしまいました、中華コピー品のUSB接続ロジックアナライザーなら持ってます

しかし今回試すCPUはarduinoなので違う方法が思い浮かびます
arduinoではmillis()やmicros()という時間取得関数を用意しているので処理の前後で時間を取得して処理時間を確認します
※ arduinoでは恐らく裏で勝手に割り込み処理が行われているので1回の実行結果をそのまま信じるのは危険です
例)

	int t[2];
	int time;
	float s1;
	float s2;
	float cal;

	t[0] = micros();
	cal = s1 / s2;
	t[1] = micros();

	time = t[1] - t[0];
・計算時間測定プログラム
シリアル受信したデータを計算して計算結果と時間をシリアル送信するプログラムです

シリアルモニタから2つの値をカンマ区切り<>くくりで送ると計算を行い結果を送信するプログラムです
データ送信:<1,2>
データ受信:1.00,2.00,,3,8,3.00,12,0.67,,-1,4,-1.00,12,0.33,,2,8,2.00,12,0.67,,0,40,0.50,32,1.25
データ内容は先頭から下記になっていて重要なのは2回目以降のカンマカンマ手前の値(long計算時間/float計算時間)です
数値1,数値2,,
long加算結果,long加算時間,float加算結果,float加算時間,long加算時間/float加算時間,,
long減算結果,long減算時間,float減算結果,float減算時間,long減算時間/float減算時間,,
long乗算結果,long乗算時間,float乗算結果,float乗算時間,long乗算時間/float乗算時間,,
long除算結果,long除算時間,float除算結果,float除算時間,long除算時間/float除算時間

long計算時間/float計算時間は1より小さければlong計算の方が速く、1より大きければfloat計算の方が速いことになります


自己流なので色々と気持ち悪いと思います(特に受信判定は自己流すら確立出来てません)

long lst1,lst2;		//入力値long
float fst1,fst2;	//入力値float
boolean ksf,s1;		//実行,符号
char jjt = 0;		//受信判定状態
float kss;			//小数点以下受信用

void setup(){
	Serial.begin(115200);
}

void loop(){
	if(Serial.available() > 0){
		char dat = Serial.read();
		if(dat == '<'){
			jjt = 0;		//どの状態でも'<'は必ず先頭とする
		}
		switch(jjt){
		case 0:		//未受信('<'待ち)
			if(dat == '<'){
				jjt++;
				lst1 = 0;
				lst2 = 0;
				fst1 = 0;
				fst2 = 0;
				s1 = 0;
			}
			break;
		case 1:		//数値1符号待ち
			if(dat == '-'){
				s1 = 1;
			}else if((dat >= '0') && (dat <= '9')){
				lst1 = lst1 * 10 + (dat & 0x0F);
				fst1 = lst1;
			}else{
				Serial.println("NG");
				jjt = 0 - 1;
			}
			jjt++;
			break;
		case 2:		//数値1整数待ち
			if((dat >= '0') && (dat <= '9')){
				lst1 = lst1 * 10 + (dat & 0x0F);
				fst1 = lst1;
			}else if(dat == '.'){
				jjt++;
				kss = 0.1;
			}else if(dat == ','){
				if(s1){
					lst1 *= -1;
					fst1 = lst1;
				}
				s1 = 0;
				jjt = 4;
			}else{
				Serial.println("NG");
				jjt = 0;
			}
			break;
		case 3:		//数値1小数待ち
			if((dat >= '0') && (dat <= '9')){
				fst1 += kss * (dat & 0x0F);
				kss /= 10;
			}else if(dat == ','){
				if(s1){
					lst1 *= -1;
					fst1 *= -1;
				}
				s1 = 0;
				jjt = 4;
			}else{
				Serial.println("NG");
				jjt = 0;
			}
			break;
		case 4:		//数値2符号待ち
			if(dat == '-'){
				s1 = 1;
			}else if((dat >= '0') && (dat <= '9')){
				lst2 = lst2 * 10 + (dat & 0x0F);
				fst2 = lst2;
			}else{
				Serial.println("NG");
				jjt = 0 - 1;
			}
			jjt++;
			break;
		case 5:		//数値2整数待ち
			if((dat >= '0') && (dat <= '9')){
				lst2 = lst2 * 10 + (dat & 0x0F);
				fst2 = lst2;
			}else if(dat == '.'){
				jjt++;
				kss = 0.1;
			}else if(dat == '>'){
				if(s1){
					lst2 *= -1;
					fst2 = lst2;
				}
				jjt = 0;
				ksf = 1;
			}else{
				Serial.println("NG");
				jjt = 0;
			}
			break;
		case 6:		//数値2小数待ち
			if((dat >= '0') && (dat <= '9')){
				fst2 += kss * (dat & 0x0F);
				kss /= 10;
			}else if(dat == '>'){
				if(s1){
					lst2 *= -1;
					fst2 *= -1;
				}
				jjt = 0;
				ksf = 1;
			}else{
				Serial.println("NG");
				jjt = 0;
			}
			break;
		}
	}

	if(ksf){
		//計算・測定
		int t[9];
		long lk[4];
		float fk[4];
		t[0] = micros();

		lk[0] = lst1 + lst2;
		t[1] = micros();
		fk[0] = fst1 + fst2;
		t[2] = micros();

		lk[1] = lst1 - lst2;
		t[3] = micros();
		fk[1] = fst1 - fst2;
		t[4] = micros();

		lk[2] = lst1 * lst2;
		t[5] = micros();
		fk[2] = fst1 * fst2;
		t[6] = micros();

		lk[3] = lst1 / lst2;
		t[7] = micros();
		fk[3] = fst1 / fst2;
		t[8] = micros();

		//excelで読み込みを考慮した1行出力
		int tx[8];
		tx[0] = t[1] - t[0];			//加算時間long
		tx[1] = t[2] - t[1];			//加算時間float
		tx[2] = t[3] - t[2];			//減算時間long
		tx[3] = t[4] - t[3];			//減算時間float
		tx[4] = t[5] - t[4];			//乗算時間long
		tx[5] = t[6] - t[5];			//乗算時間float
		tx[6] = t[7] - t[6];			//除算時間long
		tx[7] = t[8] - t[7];			//除算時間float

		Serial.print(fst1);				//数値1float
		Serial.print(",");
		Serial.print(fst2);				//数値2float
		Serial.print(",,");

		Serial.print(lk[0]);			//加算結果long
		Serial.print(",");
		Serial.print(tx[0]);			//加算時間long
		Serial.print(",");
		Serial.print(fk[0]);			//加算結果float
		Serial.print(",");
		Serial.print(tx[1]);			//加算時間float
		Serial.print(",");
		Serial.print(((float)tx[0] / (float)tx[1]));	//long計算時間 / float計算時間
		Serial.print(",,");

		Serial.print(lk[1]);			//減算結果long
		Serial.print(",");
		Serial.print(tx[2]);			//減算時間long
		Serial.print(",");
		Serial.print(fk[1]);			//減算結果float
		Serial.print(",");
		Serial.print(tx[3]);			//減算時間float
		Serial.print(",");
		Serial.print(((float)tx[2] / (float)tx[3]));	//long計算時間 / float計算時間
		Serial.print(",,");

		Serial.print(lk[2]);			//乗算結果long
		Serial.print(",");
		Serial.print(tx[4]);			//乗算時間long
		Serial.print(",");
		Serial.print(fk[2]);			//乗算結果float
		Serial.print(",");
		Serial.print(tx[5]);			//乗算時間float
		Serial.print(",");
		Serial.print(((float)tx[4] / (float)tx[5]));	//long計算時間 / float計算時間
		Serial.print(",,");

		Serial.print(lk[3]);			//除算結果long
		Serial.print(",");
		Serial.print(tx[6]);			//除算時間long
		Serial.print(",");
		Serial.print(fk[3]);			//除算結果float
		Serial.print(",");
		Serial.print(tx[7]);			//除算時間float
		Serial.print(",");
		Serial.println(((float)tx[6] / (float)tx[7]));	//long計算時間 / float計算時間

		ksf = 0;
	}
}
・実行結果
同じ数値で何度か試して見ます

1.00,2.00,,3,8,3.00,12,0.67,,-1,4,-1.00,12,0.33,,2,8,2.00,12,0.67,,0,40,0.50,32,1.25
1.00,2.00,,3,8,3.00,12,0.67,,-1,4,-1.00,12,0.33,,2,8,2.00,12,0.67,,0,40,0.50,32,1.25
1.00,2.00,,3,4,3.00,12,0.33,,-1,4,-1.00,12,0.33,,2,8,2.00,16,0.50,,0,40,0.50,32,1.25
1.00,2.00,,3,8,3.00,12,0.67,,-1,4,-1.00,12,0.33,,2,8,2.00,12,0.67,,0,40,0.50,32,1.25
1.00,2.00,,3,4,3.00,16,0.25,,-1,4,-1.00,8,0.50,,2,12,2.00,12,1.00,,0,40,0.50,32,1.25

4uSの誤差はmicros()で取得出来る値の都合のようです

1.00,1.00,,2,4,2.00,12,0.33,,0,4,0.00,8,0.50,,1,12,1.00,12,1.00,,1,40,1.00,32,1.25 1.00,2.00,,3,4,3.00,12,0.33,,-1,8,-1.00,8,1.00,,2,12,2.00,12,1.00,,0,40,0.50,32,1.25 2.00,1.00,,3,8,3.00,12,0.67,,1,4,1.00,8,0.50,,2,8,2.00,16,0.50,,2,40,2.00,32,1.25 123.00,456.00,,579,4,579.00,12,0.33,,-333,4,-333.00,12,0.33,,56088,8,56088.00,12,0.67,,0,44,0.27,32,1.37 654.00,321.00,,975,8,975.00,12,0.67,,333,4,333.00,8,0.50,,209934,8,209934.00,16,0.50,,2,40,2.04,32,1.25 123.46,654.32,,777,8,777.78,12,0.67,,-531,4,-530.86,12,0.33,,80442,8,80779.85,12,0.67,,0,44,0.19,32,1.37 -123.00,456.00,,333,4,333.00,16,0.25,,-579,4,-579.00,12,0.33,,-56088,8,-56088.00,12,0.67,,0,44,-0.27,32,1.37 123.00,-456.00,,-333,4,-333.00,16,0.25,,579,4,579.00,12,0.33,,-56088,8,-56088.00,12,0.67,,0,44,-0.27,32,1.37 -654.00,321.00,,-333,8,-333.00,16,0.50,,-975,4,-975.00,12,0.33,,-209934,8,-209934.00,12,0.67,,-2,44,-2.04,32,1.37 654.00,-321.00,,333,4,333.00,12,0.33,,975,4,975.00,12,0.33,,-209934,8,-209934.00,12,0.67,,-2,44,-2.04,32,1.37

安定して除算だけはfloat型が速いことが解ります
atmelのAVR命令一覧を見ると加算・減算・乗算は有りますが除算は有りません
この結果と情報から加算・減算・乗算はCPUの機能をそのまま使えるため整数(long)が速く、除算は整数(long)を一旦浮動小数点(float)に変換しているのではないかと予想します
追記
2018/10/31
CypressのNEW8FXシリーズでは浮動小数除算が非常に遅いようです(CPUのシリーズで書きましたが恐らくコンパイラの性能)
データを要最低限のサイズにするべきです
サンプルは少ないですが下記のような時間でした
flort型 : 300mS arduinoの9000倍?
long型 : 2.5mS
short型 : 35uS arduinoの浮動小数点除算とほぼ同レベル
小数点以下まで表示したい場合でも一旦小数点以下桁数×10倍にして計算するか「%」で一旦余りを算出して余りについて小数点以下桁数×10倍で計算するなど回りくどい処理の方が速くなります
開発環境がフリー版だからという可能性も有りますがあまりにも酷い結果です