アプリ版:「スタンプのみでお礼する」機能のリリースについて

処理が重くてプログラムが回らないので、どうにかして実行量を下げ
ようとしています。
CPUはSH2(80MHz)、内蔵メモリ(ROM、RAM)と、
外付けのフラッシュROM、RAMがあります。
OSはuItron仕様のRTOSで、プログラムはC(一部アセンブラ)
で記述しています。
作成開始前の想定では、昔、68000の12MHzで動かしていた
ソフトにシリアル通信が少し増えた程度なので、CPUの能力不足で
困るなどとは想像してなかったのですが、CPUは高速化したものの
外付けメモリの速度が足りないので外部のバスアクセスは実質10M
Hz程度しか出ないということが後で分かりました。
内蔵メモリだけで動かせば外部使用時の数倍のパフォーマンスが出る
のですが、容量が少なくてソフト全体の20%ほどしか使えません。
I2Cバス(400kbps)と子局機器とのシリアル通信(115
kbps)がメインなので、これらの割り込み処理だけでも内蔵メモ
リで駆動できないかと模索中です。
他には、関数の引数(スタック待避・復帰の処理量)を減らすなどを
試していますが、これといった効果が出ていません。
ソースは10万行くらいあり、コーディング流儀の微細な変更でも、
”積もれば山”の可能性があります。
何かいい案があったら教えてください。

A 回答 (10件)

私も今月から SH7085 + NORTi を使い始めました.


昔8ビットマイコンを自作したことはありますが,本格的な組込みの仕事は初めてです.

新しい A/D,D/A 変換基板用に別の人が作りかけていたソフトと,
その元になったソフト (別の基板用に基板メーカが作成したもの) を渡されて,
「まずハードウェアの評価をしないといけないので,とりあえず至急これを動かしてくれ」
と言われ,ようやく先日完成しました.

その過程で,あまり効率の良くないコードがたくさん目に付いたので,
製品版のソフトを開発する際には大幅に書き直してやろうと考えています.
以下,改良できそうな点のリストです.組込の専門家には常識と思いますが,
(自分用の備忘録を兼ねて) 一応ご参考まで.
(上記のソフトも組込みの専門家が書いたはずなんですが….
 組込みは初めてなので,どの程度効くかはわかりませんし,
 間違っているところもあるかもしれません.)

(1) 割込みハンドラに余計な仕事をさせている.
  複数のアナログ入力を A/D 変換したデータを多重化し,SSU で受信している (約1ms周期).
  その受信割り込みハンドラで,データのチェックとフォーマット変換を行って受信バッファに
  書き込んでいた.1ms の割込みハンドラにそんなことまでさせるなよ!
  受信データをそのままバッファに書き込んで,あとはデータ収集タスクにやらせりゃええやん!

(2) I/O レジスタを操作するのにビットフィールドを使い,
  複数のビットをセット (または複数のビットをリセット) している.例えば,

    #define SCI0 ((volatile t_SCI*)0xFFFFC000)

    // 受信エラーリセット
    SCI0.SCSSR.BIT.ORER = 0; // Overrun Error
    SCI0.SCSSR.BIT.FER = 0; // Framing Error
    SCI0.SCSSR.BIT.PER = 0; // Parity Error

   これでは SCSSR が6回もアクセスされてしまう (read/write 各3回).
   I/O レジスタのため volatile が付いており,最適化はされない.
   これを次のようにすれば2回のアクセスですむ (read/write 各1回).

    SCI0.SCSSR.BYTE &= ~(SCSSR_ORER | SCSSR_FER | SCSSR_PER);

(3) 排他制御
  ・排他制御の粒度が粗すぎる.受信割込みハンドラが書き込んだバッファの内容をを別の
   場所にコピーする間ずっと,バッファ全体を排他制御をしている.これではオーバーラン
   エラーが多発するはず.リングバッファであれば,書き込みポインタが読み出しポインタ
   を追い越さないようにするだけでいいはず (iTRON でどうやればいいのかまだわからないが).
   いやそれ以前に,コピーすること自体を見直した方がよい.

  ・排他制御すべきコード範囲が非常に短く,すぐ終わる処理ならば,セマフォなどを使わず
   一時的に割込み禁止にするだけの方が効率いいはず (マルチプロセッサではないので).
   例えば送受信などにリングバッファを使用している部分があるが,リングバッファの排他
   制御期間は非常に短くてすむので,これをセマフォではなく割込み禁止で行う.

(4) SH7085 (または SH-2) 固有の問題
  ・データキャッシュがない
   SH7085 には命令キャッシュはあるようだが,データキャッシュはないそうなので,
   (特に外部) メモリアクセスの回数を極力減らすことが必要.逆に参照の局所性は問題に
   ならず,ランダムアクセスを多用してもペナルティは発生しない (はず).したがって
   メモリアクセス順序がランダムになっても (特に外部) メモリアクセス回数を減らす
   方法があれば,そうする方がよい.(キャッシュがある場合とは逆)

  ・除算命令がない
   SH-2 には除算命令がない (1ステップ除算命令というのはあるが,これは商の1ビット
   だけを計算するものであり,商のビット数分繰り返す必要があるらしい) ので,
   特に高速化が必要な部分 (短周期割込み用ハンドラなど) ではできるだけ除算を避ける.
   ちなみに上記の 1ms 受信割り込みハンドラでは除算を (複数回) 使用していた.(-_-#)

  ・乗算命令の問題
   SH-2 の乗算命令は2ステート (32bit 乗算の場合) または4ステート (64bit の場合)
   で実行されるが,その間パイプラインがストールするうえメモリアクセス競合も発生する
   そうなので,使わなくてすむなら使わない方がよさそう.

  ・シフト命令
   ・SH-2 にはバレルシフタがない.→ 右辺が定数でないシフト演算子はなるべく避ける.
   ・SH-2 のシフト命令は,シフト数が 1/2/8/16 のものだけ.
    ただし算術シフト命令は1ビット用だけ.
    →・シフト演算子の右辺がなるべく 1/2/8/16 になるようにする.
      (ビットフィールドの配置を工夫するなど.)
     ・符号付整数のシフトはなるべく避ける.

  ・外部メモリ上のバイトまたはワード (2バイト) データのうち,同時にアクセスする
   ことが多いものはできるだけ同じ1ロングワード (4バイト) 内にまとめ,1回の
   メモリアクセスで読み書きできるようにする.
   (ただし SH7085 の外部データバスは 8/16/32 ビットから選択できるようなので,
    32ビット・データバスでなければ効果がない.)

(5) (特に多次元) 配列は添字ではなくポインタでアクセスする.
  受信バッファとして,チャネル番号,サンプル番号等を添字とする多次元配列が多用され,
  それらを添字でアクセスしている.多次元になるほど,添字から配列要素のアドレスを
  計算する手間がかかるので,なるべく添字ではなくポインタでアクセスする.

(6) パイプライン的なデータ処理の高速化
  A/D 変換したデータに対し,ノイズ除去,移動平均,平滑化などの信号処理をパイプライ
  ン的に行っている.これらは別々の関数として実装されており,したがって別々のループ
  で処理されるが,わかりやすく再利用しやすい反面,実行速度はイマイチ (のはず).

  これらの処理を1つのループに融合し,入力データから一気に最終データまで計算する
  方が,途中結果をいちいち (外部) メモリに書き出して後でまた読み込む,ということを
  何度も繰り返す必要がなくなる.また,CPU 内の演算パイプラインが効くようになるはず
  なので,その分の高速化も期待できそう.
  (余談:参照の局所性も高まるので,データキャッシュのある CPU ならさらに効果的.
   以前同様のことを画像処理 (多数の画像を演算して1枚の画像を作成する) で行った
   ことがある.)

(7) 不必要なバッファコピーが多い.
  処理の都合上,受信バッファから最終的な出力バッファの間に中間バッファが存在し,
  コピーを複数回行っている.中間バッファを使用せず,受信バッファから直接出力バッ
  ファにコピーできないか.

(8) 同じような処理がやたら多い.
  多数のバッファのそれぞれについて,それらをリングバッファとして使うためのコード
  が書かれている.リングバッファの処理を共通化すれば,コードサイズが小さくなって
  ROM の使用量が減るとともに,命令キャッシュの効きが良くなるかもしれない.おまけに
  ソースもわかりやすくなるはず.それ以外にも,共通化できる処理はできるだけ共通化する.

(9) 内蔵 RAM (32KB) に配置するデータは極力無駄を省き,できるだけ多くのデータを内蔵
  RAM に置けるようにする.ブール型の変数1個に4バイト整数丸ごと使うなんて論外!

調べればまだ他にも改善点が出てきそうですが,とりあえずこの辺で.
    • good
    • 1
この回答へのお礼

色々とありがとうございます。
いくつか期待できそうなので、さっそく試してみます。
で、近況ですが、おかげさまで、いくつか試してなんとか
予定域まで効率の引き上げつつあります。
やったことは、
1)欲しい演算処理を割り込みの中で行う。
下層タスクで演算させると、途中に割り込まれる度にジャンプ処理
とスタック操作に時間を食われて演算完了が遅れるので、いっその
こと他に影響のない範囲で押し込んでしまえばと試したら最終目的
には近づきました。(その分他がとばっちりですが)
2)頻度の多い処理だけを厳選して内蔵ROMに。
デバッグライトをソフト各部に埋め込んで1分あたりの実行回数を
計測し、上位10%を選んで内蔵ROMに詰め込んでみるとかなり
の効果がありました。
今分析結果を基に呼び元の処理を見直し中です。

お礼日時:2007/07/30 08:45

使ったことはありませんが、シリアル調歩同期にDTC(Data Transfer Controller)を


利用していますか? どうも32byteに1回CPUが処理すればいい(割り込み)みたいです。
Renesasのアプリケーションノートにありました。
URLを参照ください。

参考URL:http://documentation.renesas.com/jpn/products/mp …
    • good
    • 0

>セマフォの資源操作を実行するとカーネルの重い処理が走るのかな


>と考えているのですが、そのへんご存じないですか?

セマフォからのタスクスケジュールは、そのITRONの純粋にスケジューラ
(ディスパッチ)の性能かと思います。
といいますか、セマフォ処理は、あまり工夫しようのない部分ですね。
ミューテックスなら話は別ですが,,,

タイムラグがあるということですが、どの程度なのでしょうか?

ミスポさんはわかりませんが、itronベンダーに問い合わせれば、カーネル
性能データを出してくれるところもあります。最近は、なぜか削除されて
いますが、古いバージョンでは、マニュアルに記載しているものもあります。

ハードウエアに関係せずに純粋に測るならHEWのシミュレータでも
計測できると思います。

それからベンダーによるかもしれませんが、
1)速度をあげたい とか
2)使用メモリを減らしたい
とか要望を出すと、対応したライブラリを出してくる場合があります。
問い合わせみるのもいいかもしれません。

それから、いろいろ対応されているようなので対処済みかもしれませんが、
Nortiのマニュアルに記載がありましたが
「静的エラーチェックしないライブラリ(カーネルのことでしょう)」がある
ようですね。使っていなければ切り替えてみましょう。
    • good
    • 0

>TIC_NUMEを10程度にするとisig_timの周期が10分の1になります。


10倍の間違いでした。 訂正します。
    • good
    • 0

タイムティックが、1/1 ms(スタンダードプロファイル標準)なら 10ms程度に


間隔をあけてみるのも負荷を減らせますね。

TIC_NUMEを10程度にするとisig_timの周期が10分の1になります。
ただし、Nortiは、タイムアウトの仕様が厳密に4.0仕様に合致していなかった
ようなので、若干注意が必要かと思います。
    • good
    • 0
この回答へのお礼

ありがとうございます。
うちのシステムの計時周期は10msで、これをいじるのは無理
みたいです。
セマフォ待ちでタスクを休眠させると、待ち解除した時にスケジュ
ーラの定時起動タイミング外でもすぐに起床しますが、パラレルポ
ートから信号出してタイミングを測ると、資源解放から起床までに
少しタイムラグがあります。
セマフォの資源操作を実行するとカーネルの重い処理が走るのかな
と考えているのですが、そのへんご存じないですか?

お礼日時:2007/07/25 18:57

はずしていると思いますが、自分でもミスった経験あることとしては、


「CPUの分周をセットしていない」とかであれば、いきなり今の4倍で動いたりします。
ですが、外部メモリのボトルネックということで、これはないでしょうね。

次に、微々たる?対応方法についてですが、
ITRONカーネル速度を上げる一般的な方法として(Nortiがそうかはよく知りません)

1)タスク優先度レベルを小さくする。
  たとえば、優先度を5飛ばしや10飛ばしで定義している場合
  例) タスクA:10 タスクB:20 タスクC:30
できるだけ、最大値を小さく、詰めて定義します。
     -> タスクA:1 タスクB:2 タスクC:3
  理由:スケジューラのアルゴリズムにもよりますが、優先度別キューの上位から
     の検索を行うアルゴリズムなら、優先度200なら200回先頭キューを比較しなければ
     スケジュールしない場合が考えられるから。
     何ゆえ、最大優先度をコンフュギュレータで指定するかと言うと、固定最大値だと
     遅くなるためです。ばかばかしいかもしれませんが、実際これで回避した
     ケースの報告を聞いたことがあります。

2)優先度待ち(TA_TPRI)をやめて、できるだけFIFO待ちにする。
  優先度待ちのアルゴリズムは大抵先頭から順番に比較するというあまり賢くないもの
  です。優先度待ちに多く並んでいた場合にさらに待つとカーネルが多くのCPU時間を
  使います。
TOPPERS/JSP FI4もこれです。というよりそうでないものは知りません。

3)周期ハンドラアラームハンドラのID数を減らします。
  周期ハンドラの定義数分だけ、isig_tim時に分まわります。(そうでないのもあります,TOPPERSはその辺賢いです)
  定義だけというのもやめて必要数だけ生成する。

4)可変長メモリプール使わず、できるだけ固定長メモリプールでサイズを分けましょう
  可変長メモリプールのメモリが返されたときのマージ処理は非常に重い場合があります。

5)多重割り込みを禁止できるなら禁止にする。
  割り込みハンドラや、周期、アラームハンドラなど、多重割り込みが可能にしてあるなら
  禁止にする。そうすることで多重割り込みにかかわる負荷が減ります。ただし、割り込み
  処理が逐次処理されるので割り込みが間に合わない場合は禁止できないです。

6)カーネル管理外割り込みを利用する
  サービスコールを使わない割り込み処理でいいならカーネル管理外割り込みとすれば
  カーネルが動作しない分だけ処理が軽くなります。
    • good
    • 1
この回答へのお礼

ありがとうございます。
1~3は実施済み、4は返却しないのでカーネルに任せず自主管理。
5は間に合わないので無理、6もカーネルを介してません。
TOPPERSはまだ使ったことないんですが、やはり対処は同じような
感じなんですね。

お礼日時:2007/07/25 18:48

私も#3の回答にあるような原因ではないかと思うのですが...



とりあえず小手先の改善策だけでも書いておきます。

1. スタックを内蔵メモリに配置する。
2. 最適化オプションは、プログラムサイズが「最小」になるように設定する。
3. カーネルやライブラリを内蔵メモリに配置する。
4. 静的記憶域期間のデータには、intやlongではなく、可能な限りshortやcharを使用する。
5. 複数ビットのビットフィールドはなるべく使用しない。

他はソースをざっとでも見ないと判断できませんね。

ところで、外部のバスアクセスが10MHzしか出せないというのは、ハードウェアの設計ミスのような気がします。
    • good
    • 0
この回答へのお礼

ありがとうございます。
今その辺を修正中です。
2,4,5はメモリアクセス量を減らすための手法という解釈で
いいですか?

お礼日時:2007/07/25 08:19

なんとなくの印象ですが、


処理を急ぎすぎて、かえって速度を落としているパターンだったりしないかなぁと。

処理を急ぎたいがために、
割込みで実処理を行ってしまったり、
タスクの優先順位を不用意に上げたりして、
かえってパフォーマンスを落としているとか。

割込みで行っている処理というのは、具体的にどんな処理なんでしょうか。
    • good
    • 0
この回答へのお礼

ありがとうございます。
> 割込みで行っている処理
シリアルの送受信レジスタ制御
I2C送受信レジスタ制御は
RS485の送信イネーブルのカット
A/D変換ICの読み出し
メインはこんなところです。

お礼日時:2007/07/25 07:54

ITRONユーザです。

NORTi 使ってます。

> CPUは高速化したものの外付けメモリの速度が足りないので外部
> のバスアクセスは実質10MHz程度しか出ないということが後で
> 分かりました。

SH2って、内部クロック(Iφ)、バスクロック(Bφ)、周辺クロック(Pφ)、およびMTU2 モジュール用クロック(MPφ)をそれぞれ設定できるんですよね。SH2のどの型式かわかりませんが、CPUコアが80MHzなら、バスクロックは40MHzくらいまでいけるようですね。(SH7146のハードウェアマニュアルを見ながら回答書いています。) 詳しくは、お持ちのSH2のハードウェアマニュアルの、クロック発信機(CPG)の章を参照してください。

って、実はバスの速度じゃなくて、外付けしたRAMのほうが遅くて、っていう話でしたっけ? 外付けはDRAMですか?

ソースコードの行数からして、素人ではなくお仕事でプログラムを書かれている方だと思いますので、割り込みの中にいろいろ書いちゃうような変なことはしていないと思いますが、いかがでしょうか。

差し支えなければ、SH2の型番、各タスクとその優先度、タスクに割り当てたスタックサイズ、処理周期、全体で必要なRAM容量など書いていただければ、また違った回答があるかもしれません。

Sectionでどこにどのメモリを割り当てているかとか。処理速度に効きそうですよね。高速化が必要な部分のメモリだけ内部メモリに割り当てるなんていう案を今思いつきました。

> ソースは10万行くらいあり、コーディング流儀の微細な変更でも、
> ”積もれば山”の可能性があります。

プログラムの処理速度にもパレートの法則が当てはまるものと思います。ですから、ボトルネックとなっている2割のコードを改善すれば8割の改善は見込めるんじゃないでしょうか。

この回答への補足

言い忘れてました。^^;)
ありがとうございます。
クロックセッティングはIφ=80MHz,Bφ=Pφ=MPφ=MIφ=40MHz
(CPU使用範囲で最速)です。
外付けRAMは70nsのルネサス製SRAMで、基板に付けた電池で
バックアップして停電保護メモリも兼ねています。
12nsの高速タイプもあるのですが、消費電力が桁違いでバックアップ
の寿命が足りないためハード担当と思案中です。

補足日時:2007/07/25 09:36
    • good
    • 0
この回答へのお礼

主用途は電圧値のデジタル変換と監視(比較信号の出力)で、
タスクは最優先から、
電圧値のデジタル変換、パラレルIO制御:10ms定周期
キー検出(別基板からのシリアル受信):30ms遅延
ウォッチドグ制御:100ms定周期
電圧値の演算と信号比較:200ms定周期
シリアル(子局からの電圧値受信):100ms定周期
シリアル(その他アプリ用):50ms遅延(*3本)
画面制御(表示基板へのシリアル送信):100ms遅延
エラー監視:200ms遅延
自己診断(定期のハードチェック):500ms遅延
USBポート制御:不定期(*2本)
こんな感じです。
上層のタスクが排他で”待ち”(休眠)に入っているのなら、
下層のタスクはまわるはずなんですが、めいっぱい働いてしま
っているので下がまわらないようです。

お礼日時:2007/07/24 08:53

処理が重くなった原因が外部メモリの速度にあることははっきりしているのでしょうか? (少し増えたといわれる)シリアル通信に関連して、セマフォ等による排他制御が足を引っ張っていることも考えられます。


また、処理速度を何パーセント程度向上したいのでしょうか? それによっても選択肢が変わってきます。

いずれにしても、実際のソースをざっとでも見ないと適切な判断が出来ませんが、そうもいかないと思うので、可能な限りの情報を提供してください。
CPUの型番、RTOSやコンパイラの製品名も補足してください。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
CPUはSH7085、コンパイラはルネサスのSHC(Ver4.0)、
OSはミスポのnortiです。
I2Cが10ms毎に40byteほど、シリアルは115kで100ms
毎に200byte程度と、19.2kで100ms毎に80byte、
他に400kの同期1本と9.6kの調歩が3本。
通信のハード制御は、カーネル資源では制御が間に合わないので、ほとんど
割り込みで駆動させています。
現在無駄処理がないかソースを検索中ですが、I2Cを組み込んだ時に、カ
ーネル資源を多用するとディスパッチが重くなることが分かって、ほとんど
削除したので無駄な排他が見つかる見込みは少ないです。
向上の希望量は、”可能な限り”です。現状の2倍以上のパフォーマンスが
ないと、最下層のタスクで制御させているUSBポートがメモリを装着して
も認識してくれません。
115kのシリアルを入れるまでは最下層のタスクも悠々だったのですが、
入れた途端に全然言うことを聞かなくなり、排他か割り込みの制御ミスか
と調査したら時間不足になったようです。

お礼日時:2007/07/24 00:30

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!