超悩み

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
みどり

超悩み

#1

投稿記事 by みどり » 17年前

みなさんお元気ですか。

残暑はまだまだですね。
さて、長い間悩んできたことあります。

それはC言語においての配列初期化です。
例えば、float型の配列 F[10000]を特定の値, 1.2345で初期化したい場合、
毎回毎回一万回のループを走らせてしなければならない。
ループが文面というより、能率のほうは気になります。

memset()のような内臓関数を使いたいですが、それはint値を対象としているので、
利用できないようです。

何か利用できる其の他の内臓関数ありますか?

宜しくお願いします

バグ

Re:超悩み

#2

投稿記事 by バグ » 17年前

memsetも中ではループ処理を行ってますので、結局は同じことなのではないでしょうか?
下記は、某サイトに掲載されていたmemsetの実装例です。
これを参考にfloat型に対応したmemsetを作られては如何でしょうか?
void *memset(void *s, int c, size_t n)
{
    const unsigned char uc = c;
    unsigned char       *p = (unsigned char *)s;

    while (n-- > 0)
        *p++ = uc;

    return (s);
}

御津凪

Re:超悩み

#3

投稿記事 by 御津凪 » 17年前

少し強引な方法ですが、下記の方法で強制的に float 型から int 型に変換できます。
float f = 1.2345f;
int i = *((int*)((float*)&f)); // float から int に変換
float f2 = *((float*)((int*)&i)); // int に変換した float 値を戻す
printf("float:%d,int:%d\n",sizeof(float),sizeof(int)); // float と int の型のサイズを確認
printf("f:%f,i:%d,f2:%f\n",f,i,f2); // きちんと変換されているか確認
( float が int 以下であるサイズである場合のみ有効な手段です)
これを利用して、強制変換した i を、
memset(buf,i,sizeof(buf));
とすればOK…ではないです。(今確認しました)
memsetの対象値は int 値でも、セットされる値は 1Byte です。
つまり、上記の処理をしても正しくセットされません。

と、言うわけで、初期化した float 型の配列をもっておいて、
初期化時に、 memcpy を使うと良いでしょう。
(上記の変換方法は今回は意味ありませんがせっかく書いたので載せておきます)

> 内臓関数
なんか生々しい関数ですねw
正しくは標準関数ですよ。

Re:超悩み( 訂正 )

#4

投稿記事 by » 17年前

皆さんご返答ありがとうございます。
いろいろ実験中ですが。

====================================
ごめんなさい!
大変大きなミスをしました

> 内臓関数
内蔵関数でした。

memset()のような関数は
そのマシンの一番実行能率のいい機械コードで書かれた関数という記憶ありますが。


またよろしくお願い致します

ibis

Re:超悩み( 訂正 )

#5

投稿記事 by ibis » 17年前

>内蔵関数

標準ライブラリに含まれる関数は標準関数と言った方が良いかと。
内蔵関数の定義を知らないのですが、少なくとも一般的な表現ではないですよね。
検索してみたら関係ないものばっかり出ますし。



>そのマシンの一番実行能率のいい機械コードで書かれた関数という記憶ありますが。

そうでもないですね。

まず、普通は機械語ですら書かれていません。
汎用性が下がりますし、Cプログラマがそれを読めないことも多々ありますし。

それにメモリや速度のトレードオフがあるので、どのコードが最も能率が良いかというのは一概には決められませんしね。
標準関数である以上、ライブラリの実装とコンパイラの最適化に依存します。

ちなみにVC++には組み込み形式の関数(組み込み関数とも呼ばれるが、一般に「組み込み関数」と呼ばれるものとは違う)もあり、それは通常の関数よりも少し速いですが、最速というわけではないです。



「メモリや速度のトレードオフ」と書きましたが、この場合は以下のように考えればいいでしょうか。
・実行速度を最適化するなら、御津凪さんが仰った、memcpy(又はそれに似た組み込み関数)を使う方法。
 VC++なら、組み込み形式のmemcpyが最速かもしれない。それを使う場合、メモリ使用量はコード中の呼出し回数に応じて増加。
・メモリ使用量を最適化するなら、ループを使う方法。

Justy

Re:超悩み( 訂正 )

#6

投稿記事 by Justy » 17年前


>memset()のような関数は そのマシンの一番実行能率のいい機械コードで書かれた関数

 環境次第なのでそういう傾向がないことはないですが、どういう面から見た
効率なのかは様々でしょう。

 昔某環境で逆アセンブルして見たのだと、渡されたポインタが16byteアライメントに
乗っているときや、残りのサイズ数で分岐してコピー方法を変えている実装のを
見たことがあります。
 ぱっと見た目遅そうなのですが、アライメントに乗った大容量のコピーに
強そうな感じでした。

 逆に言えば、数バイトのコピーに使うには無駄が多いわけで・・・。

たかぎ

Re:超悩み( 訂正 )

#7

投稿記事 by たかぎ » 17年前

>  逆に言えば、数バイトのコピーに使うには無駄が多いわけで・・・。

そういうことです。

最適化性能が高い処理系では、memsetは組み込み関数になるのが普通です。
そして、コンパイル時にサイズが分かるのであれば、それに応じてコンパイル結果を変えます。

例えば、数バイト程度であれば、ループとかではなく、ベタベタに展開した方が効率がよくなります。
ストリング命令、ブロック転送命令などがあるアーキテクチャではそれを使うことでしょう。
いずれにせよ、関数呼び出しに関わるオーバーヘッドを省くことが可能です。

MCUなんかだと、DMA転送とかを使えばもっと高速に実装できる可能性がありますが、型番間の互換性がなくなったり、DMAチャネルを処理系に予約しないといけなかったりするので、使っているのは見たことがありません。
あと、ブロック転送命令があっても、例えばH8のEEPMOVなんかは、実行中に割り込みが入らなくなるので使われないようです。いくら高速でも、どれだけ時間がかかるか(コンパイル時には)わからないので、その間ずっと割り込みを禁止するわけにはいきませんから。

ちなみに今回の件に関しては、C++であればstd::fill_nを使うのが原則です。Cの場合はループで書くしかないでしょう。
効率に関してはどちらも変わりません。

memcpyを使う場合には、レジスタ → メモリだけでなく、メモリ → レジスタの操作も必要なので、単純に考えても倍以上時間がかかります。
一般に、ライトよりリードの方が時間がかかるのです。なぜなら、ライトの場合は(ハード的に)バッファリングが可能ですが、リードの場合はその値を直後から必要とする可能性が高いため、正直に読み込まないといけない上、パイプラインも乱れるからです。

閉鎖

“C言語何でも質問掲示板” へ戻る