26 Oct 2013, 12:54

組込み開発の二大迷信に挑む!リファクタリングにおけるパフォーマンスとスタックオーバーフローについての数値実験

組込み開発において、リファクタリングしようとすると、自分は怒られる。

  • パフォーマンスが低下する
  • スタックオーバーフローする

怒られるのが嫌で、リファクタリングできない。この2大迷信について、簡単な実験してみた。

Normalコード(Test1)

これは普通のコード。ここを参考にした。clock

[tsu-nera]% cat timer.c
#include <time.h>
#include <stdio.h>

int main(void)
{
  clock_t start, end;
  long l;
  long i=0;
  int  n=0;
  clock_t total_start, total_end;

  total_start = clock();

  while(n < 5){

    start = clock();
    i = 0;

    for (l=0; l<100000000; l++) {
      i++;
    }

    end = clock();
    printf("ループ1億回の時間: %f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
    n++;
  }

  total_end = clock();

  printf("平均の時間: %f秒\n", (double)(total_end - total_start) / CLOCKS_PER_SEC / n);

  return 0;

}

Functionコード(Test2)

つづいて、関数でインクリメントを抽出したコード。

[tsu-nera]% cat timer2.c
#include <time.h>
#include <stdio.h>


void incriment(long* i) {
  (*i)++;
}

int main(void)
{
  clock_t start, end;
  long l;
  long i=0;
  int  n=0;
  clock_t total_start, total_end;

  total_start = clock();

  while(n < 5){

    start = clock();
    i = 0;

    for (l=0; l<100000000; l++) {
      incriment(&i);
    }

    end = clock();
    printf("ループ1億回の時間: %f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
    n++;
  }

  total_end = clock();

  printf("平均の時間: %f秒\n", (double)(total_end - total_start) / CLOCKS_PER_SEC / n);

  return 0;

}

実験結果

気持ち、Test1のほうが早い気がする。もっと実験すれば、大数の法則で正確な値がでそうだけど、まあいいや。とりあえず、気持ちの問題で小さな関数はパフォーマンスをあまり低下させないという自身がついた。

Test1

[tsu-nera]% ./a.out
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 0.720000秒
ループ1億回の時間: 0.670000秒
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 1.170000秒
平均の時間: 0.932000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.030000秒
ループ1億回の時間: 0.960000秒
ループ1億回の時間: 0.960000秒
ループ1億回の時間: 0.770000秒
ループ1億回の時間: 0.680000秒
平均の時間: 0.882000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.250000秒
ループ1億回の時間: 0.840000秒
ループ1億回の時間: 0.670000秒
ループ1億回の時間: 0.570000秒
ループ1億回の時間: 0.630000秒
平均の時間: 0.792000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.030000秒
ループ1億回の時間: 0.730000秒
ループ1億回の時間: 0.630000秒
ループ1億回の時間: 0.520000秒
ループ1億回の時間: 0.460000秒
平均の時間: 0.674000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 0.940000秒
ループ1億回の時間: 0.720000秒
ループ1億回の時間: 0.630000秒
ループ1億回の時間: 0.540000秒
ループ1億回の時間: 0.500000秒
平均の時間: 0.666000秒

Test2

[tsu-nera]% ./a.out
ループ1億回の時間: 1.010000秒
ループ1億回の時間: 0.810000秒
ループ1億回の時間: 0.650000秒
ループ1億回の時間: 0.560000秒
ループ1億回の時間: 0.480000秒
平均の時間: 0.702000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.470000秒
ループ1億回の時間: 1.160000秒
ループ1億回の時間: 0.930000秒
ループ1億回の時間: 0.700000秒
ループ1億回の時間: 0.590000秒
平均の時間: 0.970000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 0.870000秒
ループ1億回の時間: 0.710000秒
ループ1億回の時間: 0.770000秒
ループ1億回の時間: 1.270000秒
平均の時間: 0.934000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.200000秒
ループ1億回の時間: 0.890000秒
ループ1億回の時間: 0.710000秒
ループ1億回の時間: 0.650000秒
ループ1億回の時間: 0.540000秒
平均の時間: 0.798000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.020000秒
ループ1億回の時間: 0.760000秒
ループ1億回の時間: 0.620000秒
ループ1億回の時間: 0.550000秒
ループ1億回の時間: 0.560000秒
平均の時間: 0.704000秒

スタックオーバーフローのテスト(Test3)

関数を呼びすぎるとスタックオーバーフローするよと脅されたが、脅した人はどのくらいの確信をもって発言したのかをテストした。

こんなサンプル。

#include <stdio.h>

void incriment(long* i) {
  (*i)++;
  printf("i=%d\n",*i);
  incriment(i);
}

int main(void)
{
  long  i=0;
  incriment(&i);
  return 0;

}

テスト結果

大体、自分のCygwin環境だと400000くらいでクラッシュする。

i=392880
i=392881
i=392882
[1]    18958 segmentation fault  ./a.out

ただ、スタックオーバーフローは、タスクサイズと環境依存なので、一概に安心はできないな。3階層くらいならば、へのようなものか(・・?

結論

関数抽出しても、コンパイラが最適化してくれるため、パフォーマンスは気にしない。小さな関数はコンパイラがinlineしてくれる。20年前の常識は、現代の非常識。

ただし、スタックオーバーフローは注意を払う。

26 Oct 2013, 10:27

大規模コードをリファクタリングする方法『ミカドメソッド(Mikado Methood)』について

TDDの技法で、mikado method(ミカドメソッド)という、なにやら怪しい名前の方法が流行ってるらしいので、調べてみた。

ミカドメソッドとは

ミカドメソッドとは、大規模なコードをリファクタリングするための方法論。

本もある。

動画もある。

Large-scale refactorings using the Mikado Method – Ola Ellnestam & Daniel Brolund from Øredev Conference on Vimeo.

[//www.youtube.com/embed/OTccRWjriOM?rel=0]

 ミカドメソッドの由来

Mikado(ミカド)メゾッドの名前は、ヨーロッパでポピュラーなテープルゲームが由来らしい。ミカドは、天皇(帝」である。

ちなみに、日本をバカにした『ミカド』というオペレッタもあるというのはどうでもいい雑学。

ミカドメソッドの方法

以下のステップに従う。

  1. ミカドゴール(最終到着目標)を決める
  2. おバカな方法でまずはゴールを実装する。
  3. エラーをたくさんみつける。
  4. 速やかにエラーを除去する。
  5. 新しい解決方法を必要条件として導き出す。
  6. エラーが取れなかったら、初めのスタート地点に戻る。
  7. 2から6を繰り返していく。
  8. エラーがなくなったか確認。

数学用語の『必要条件』を利用して目標を展開していく。PはMikadoの必要条件。

Mikado => P

MikadoのためにはA、B、Cが必要。Bのためには、E、Fが必要など。必要条件の展開は、ミカドグラフ?に描いていく。

Micado
======
| | |
A B C
  |
 E F

いくらでも失敗してよい、失敗したら初めに戻ればよい。はじめに戻る方法は以下。

git reset --hard && git clean -f

成功した道をミカドグラフに残していく。この失敗を恐れぬ大胆な挑戦と、確実な道筋を描いていくことが、大規模リファクタリングに適しているのだとか。あまり、書籍で読んだだけでは実感がわかないな。

ということで、練習用のリポジトリがgithubにある。練習方法は以下の記事が参考。この記事を書いているときは、やっていないけれども、あとで挑戦してみる。

26 Oct 2013, 01:22

Eclipseが立ち上がらないときは.logを見て調査する

Eclipseが立ち上がらなくなったので、調査方法と解決方法をメモ

とりあえず eclipse.exe -cleanで

運が良ければ、cleanオプションをつけてEclipse再起動すればなおる。

> eclipse.exe -clean

eclipse.exeが置いてあるフォルダのeclipse.exe -clean.cmdを実行すれば良い。

Eclipse Errorログを見る

Eclipseのエラーログは以下にある。

(YourWorkspace)/.metadata/.log

問題が起こったら、とりあえずログを見て調査する。該当時間のメッセージをググる。

Eclipse コンソールモードで立ちあげ。

eclipse.exeが置いてあるフォルダのeclipsec.exeを実行すると、コンソールアプリケーションとして起動することができる。

そこに現れる標準出力をググる。

ビューの設定をリセット

今回の原因。

!MESSAGE Job found still running after platform shutdown.  Jobs should be canceled by the plugin that scheduled them during shutdown:

以下のファイルを削除すればよいようだ。

(YourWorkspace)\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi

これは、Eclipseの起動状態を管理しているファイルのよう。ビューが開けなくなったときも、このこの workbench.xmiを削除(orリネーム)して、-clean コマンドをつけて再起動すれば良い。

25 Oct 2013, 04:14

TopCoder用テンプレート(C++)

TopCoder用テンプレート

自分のテンプレートを公開。随時追加中。

補助関数

よくつかう補助関数をまとめます。補助関数を利用するには、 以下をインクルード。

#include<algorithm>
using namespace std;

最大・最小

int = 1, int j = 2;

// 最大値
rslt = max(i,j); // rslt = 2

// 最小値
rslt = min(i,j); // rslt = 1

スワップ

ポインタを指定する必要はありません。sortの実装の時などに。

// 交換
swap(i,j) // i=2,j=1

25 Oct 2013, 04:06

C++での動的配列(set,map)の使い方まとめ

C++での動的配列(set,map)の使い方についてまとめます。

setの使い方

setとは、自動でソートされる動的配列を扱うためのSTLです。

数学でいうところの”集合”に対応するものですが、setは要素数が有限です。

宣言

#include <set>
using namespace std;

set <string> s;

関数

要素数を調べる
s.size();
要素を追加する
s.insert("hoge");

同じ要素を追加しても、要素数は1つのまま。

s.insert("hoge");
s.insert("hoge");
s.size(); // = 1
要素を検索する
s.find("探すもの")

if( s.find( "hoge" ) == s.end() )
{
    cout << "Not Found" << endl;
}
else
{
    cout << "Find" << endl;
}
要素をすべてクリアする
s.clear();

よくある使い方

文字列Sのなかの文字の要素数を調べる。

int getNumber(string S) {

  set <string> s;
  for(int i=0; i<S.size(); i++) {
    s.insert(S[i]);
  }
  return s.size();
}

pairの使い方

pairは2つの値をペアで扱うためのSTLです。

宣言
#include <utility>
using namespace std;

pair <string, int> p;
参照
p.first();  // 一つ目の要素
p.second(); // 2つ目の要素
要素を追加する
p.first  = "hoge"
p.second = 3

pair<int, string>( "hoge", 3 );
p = make_pair("hoge",3);

mapの使い方

map とは、連想配列を扱うための STLです。

(キー, 値)のペアでデータが保持されます。「任意の型のキー」から「値」を引く辞書を作成できます。

setは(キー)のみを要素にしていますが、mapはpairを要素とします。

宣言
#include <map>
using namespace std;

map <string, int> m;
参照
m.first();  // キーへのアクセス
m.second(); // 値へのアクセス
要素を追加する
s["hoge"] = 3;
s.insert(pair<string, int>("hoge", 3));
s.insert(make_pair("hoge", 3));
要素を検索する

setと同じなので、省略。

要素をクリアする
m.clear();

よくある使い方

mapの要素にアクセスするには、イテレータを利用するのが便利です。

map<string, int>::iterater it;

また、要素の先頭アクセスするには、begin(),最後にアクセスするにはend()を利用します。

m.begin(); // 先頭要素にアクセス
m.end();   // 最後の要素にアクセス

文字列Sのなかの文字で一番数が多いものの最大値を求めます。

int getNumber(string S) {
    map <string, int> m;
    for(int i=0; i < S.size(); i++) {
      m[S[i]]++;
    }

    int max = 0;
    map<string, int>::iterator it;
    for(it = m.begin(); it!=m.end(); it++ ) {
      if( max < it->second ) max = it->second;
    }
    return max;
}

22 Oct 2013, 12:26

C言語でのローカル変数宣言は、ブロックの途中でも宣言可能らしい(C99)

C++の勉強をしていると、こんなことをよく言われる。

C++では、ローカル変数はメソッド内のどこでも宣言できる。C言語はブロックの先頭で宣言しなければいけない。そこがCとC++の差だ。

実は、そんなことはなかった!C言語でも、ローカル変数を関数内のどこでも宣言できる。

むかしは、ローカル変数は関数の頭で宣言しないといけなかった。しかし、C99で仕様拡張があった。

C99以降のC言語ではローカル変数を関数内のどこでも宣言できるようになったらしい。スコープは、その宣言された直後から開始される。ただし、VisualC++2008/2010/2012 のいずれも、この機能には対応してい無いらしい(これは未検証」

たとえば、こんなのもエラーしない・・・?

#include <stdio.h>
int main(void)
{
  printf("hogehoge\n");
  for(int i=0; i<10; i++)   printf("%d",i);
}

エラーした。

$ gcc local_val.c
local_val.c: 関数 ‘main’ 内:
local_val.c:4:3: エラー: ‘for’ ループ初期化宣言は C99 モード内でのみ許可されています
   for(int i=0; i<10; i++)   prinntf("%d",i);

コンパイラは、 gcc 4.8を利用。

$ gcc --version
gcc (GCC) 4.8.1

こんどは、C99をサポートするコンパイルオプションをつけると通った。

$ gcc -std=c99 local_val.c

リーダブルコードでも、スコープを狭めたほうが可読性が上がるし、こっちの仕様のほうがいいな。

21 Oct 2013, 13:20

『C言語とC++』メリットとデメリットについて調べてみたメモ

Courseraで 『C++ for C Programer』という講義を受講しています。そこで、今日は、C++がC言語よりも優れている点、またはその逆を調べてみました。

講義では、マーガリンはバターから作られるから、バターよりベターというオヤジギャグでC++のCに対する優位性が説明されるが、ほかにも以下の様な理由があげられる。

  • More Type Safe
  • More Libraries
  • Less reliance on preprocessor
  • OO vs imperative

そこで、ネット上の意見も調べてまとめてみた。

※自分の意見ではなくて、あくまでネット上の情報をまとめたもの。

C++がCよりも優れている点

  • C++はC言語を内包している、互換性がある。
  • オブジェクト指向を言語がサポートしている。
  • 豊富なライブラリ群

CがC++よりも優れている点

  • 実行速度、パフォーマンスが高い。
  • メモリ使用量の小ささ。
  • C++よりも文法がシンプル。

まとめ

ハードのレジスタをいじるような組込み系はCが優位、アプリケーションならばC++が優位と読めた。

C言語でもオブジェクト指向ライクな実装はできるけれども、それをしないのは、(すくなくとも自分の職場では)、過去の資産が手続きで書かれているためと、OOに対する知識のなさからだと思う。

あとは、OOライクでないと、テストコードがとても書きにくい。TDDが好きなので、C言語よりもC++に魅力を感じる。いまはC言語を仕事でつかっているけれども、早くC++を身につけてC++の仕事に移動したい。

20 Oct 2013, 01:00

必殺技を実践で繰り出すために、TDDの『Code Kata』について調べてみた

TDDの型を身につけるための方法論『Code Kata』について調べてみたことをまとめます。

medium_7380838634

photo credit: 1936matt via photopin cc

きっかけ

本『The Coding Dojo Handbook』を見つけたので、調べてみた。この本の読書メモは読み終わってからまた書く。

 The Pragmatic Bookshelf | The Coding Dojo Handbook

これらの本にも、紹介されてた。初め、Dojosをどぜうだと思っていた。




自分も過去記事にしてた。 TopCoderに挑戦するきっかけになったのだった。再度、引用。

Code Kata てなに?

Kataは、日本の柔道の型を指す。

実践で技を繰り出すためには、普段からなんどもなんども技を練習しておく必要がある。実践では、考えてる時間がないので、頭が空っぽでも身体がかってに動くレベルまで、普段から基本的な技を磨いておく。

これを、TDDに応用したのが、『Code Kata』だ( ー`дー´)

TDDやリファクタリングの技術は小さなTipsや心構えの積み重ねなので、それを実践のコーディングですばやく繰り出すためには、普段からTDDをして身体を慣らしておく必要がある。『CleanCoder』のボブおじさんの言葉も引用。

必要になった時に完全な動きが自動的にできるようになることが、最終的な目標である。

プログラミングの型というのは、プログラミングの問題を解くためのキーボードやマウスの動きの練習である。

解き方はすでにわかっている問題を解きながら身体の意思決定の練習をするのだ。

(第6章 練習)

やり方

お題がネット上にあるので、それを解く。自分は、『The Coding Dojo Handbook』のお題を進めていく予定。

週1回から3回、30分くらいかけて実施する。深い呼吸をしながら、心の声に耳を傾ける。

まとめ

型を磨くというのは、プログラミングにかぎらず、道を極めるための王道だ。『Code Kata』というのは、Katalogという形で、技を磨くためのステップが体系化されているところがよい。

しばらく、この習慣を始めてみようと思う。

19 Oct 2013, 07:28

iPadでデュアルディスプレイを可能にする『AirDisplay 2』にアップデート。速度が50%向上!

いつも常用しているiPadソフト、『AirDisplay』が2にアップデートしていたので、試してみました。

20131019160113.jpg

このソフト、いままでは速度が遅かったり、解像度が途中でおかしくなったり、そもそもたまに接続できなかったりで、常用しているわりにはとてもストレスのたまる子でした。



でも、今回のアップデートで、

  • スピードサクサク(50%の速度向上!)
  • 解像度問題なし
  • 一発で接続可能

最高ですね。詳しいアップデート内容は以下。

クライアントソフト、iPadアプリ共に、デザインも,フラットデザイン時代を狙った、モダンなものになってます。

20131019162121.jpg

ただし、接続先のIPが192.168.1.101になっていることが気に食わない。これは別のIPをすでに割り当てているので、どうしようか。。。

参考までに、モバイルwifiだと、以下のような対処方法が必要。

いつも常用しているiPadソフト、『AirDisplay』が2にアップデートしていたので、試してみました。

20131019160113.jpg

 

このソフト、いままでは速度が遅かったり、解像度が途中でおかしくなったり、そもそもたまに接続できなかったりで、常用しているわりにはとてもストレスのたまる子でした。



でも、今回のアップデートで、

  • スピードサクサク(50%の速度向上!)
  • 解像度問題なし
  • 一発で接続可能

最高ですね。詳しいアップデート内容は以下。

クライアントソフト、iPadアプリ共に、デザインも,フラットデザイン時代を狙った、モダンなものになってます。

20131019162121.jpg

 

ただし、接続先のIPが192.168.1.101になっていることが気に食わない。これは別のIPをすでに割り当てているので、どうしようか。。。

参考までに、モバイルwifiだと、以下のような対処方法が必要。

19 Oct 2013, 03:56

gcc 4.8.1を利用するためにCygwin64に移行したメモ + Eclipse CDTの設定について

Cygwin 64bit版が 2013年の7月にリリースされたようです。Cygwin64bit版のgccは、c++0xに準拠したgcc 4.8.1をサポートしているようです。

コレを試してみたかったので、64bit版に移行してみました。ついでにEclipse CDTでCygwinを利用したC++開発環境をセットアップするまでの作業メモです。

64bit版のインストール

Cygwin公式サイトにいくと、以下の2つのインストーラがあります。

  • Cygwin
    • setup-x86.exe (32-bit installation)
    • setup-x86_64.exe(64-bit installation)

以前は、setup.exeの一つでした。ここでは、64bit版のsetup-x86_64.exeをダウンロードします。

インストーラを起動して、次へ次へと選択。ここでインストール先のフォルダパスが、defaultではcygwin64となっていることに気づきます。

いままで利用していたディレクトリ名がC:\cygwinだったので、ここは旧フォルダとの互換性を保つために、インストール先をC:\cygwinに変更します。合わせて、もともとのC:\cygwinは C:\cygwin86と名前変更しました。

リポジトリは、.jpが付いているところを選択。

  • ftp.yz.yamagata-u.ac.jp
  • ftp.jaist.ac.jp

あとは必要なパッケージを見繕ってインストール。ここでは、C++開発に必要な最低限をまずはインストールする。

  • Devel/gcc-core
  • Devel/gcc-g++
  • Devel/make

gccのバージョンは、gcc 4.8.1です。バッチリ v(-∀-)v

$ g++ --version
g++ (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.

/home/配下のファイルを旧環境から新環境に移行。これで、.sshやら.emacs.dやらが一切合切移動できる。

その他、Cygwinを導入したらこのまとめ記事が参考になりすぎる。

Eclipse CDT の設定

Eclipse CDTの導入は省略。Pleiadesから落とせばよい。

プロジェクト・エクスプローラから 新規C++プロジェクトを作成。ここで、ツールチェーンには、Cygwin GCCを選択。

Eclipse を -cleanオプションを再起動すると、4.8.1のパスをインクルードしてくれる。(cleanしないと、古い設定が残ったままになる)

apt-cygの対応

おまけで、Cygwin入れたら欠かせないツール、’apt-cyg’も導入する。