32

1 バイト (char) より大きい値を設定する memset() のバージョンはありますか? たとえば、 memset32() 関数があるとします。これを使用すると、次のことができます。

int32_t array[10];
memset32(array, 0xDEADBEEF, sizeof(array));

これにより、配列のすべての要素に値 0xDEADBEEF が設定されます。現在、これはループでしか実行できないようです。

具体的には、memset() の 64 ビット バージョンに興味があります。そのようなことを知っていますか?

4

8 に答える 8

36
void memset64( void * dest, uint64_t value, uintptr_t size )
{
  uintptr_t i;
  for( i = 0; i < (size & (~7)); i+=8 )
  {
    memcpy( ((char*)dest) + i, &value, 8 );
  }  
  for( ; i < size; i++ )
  {
    ((char*)dest)[i] = ((char*)&value)[i&7];
  }  
}

(説明、コメントで要求されているように:ポインターに割り当てると、コンパイラーはポインターが型の自然な整列に整列していると想定します。uint64_tの場合、それは8バイトです。memcpy()はそのような仮定をしません。一部のハードウェアでは整列されていません。アクセスは不可能であるため、アラインされていないアクセスがハードウェア上でわずかなペナルティまたはペナルティなしで機能すること、またはそれらが発生しないこと、あるいはその両方がないことを知らない限り、割り当ては適切なソリューションではありません。コンパイラは小さなmemcpy()とmemset()を置き換えます。 sより適切なコードを使用しているため、見た目ほどひどいものではありませんが、割り当てが常に機能することを保証するのに十分な知識があり、プロファイラーがより高速であると通知した場合は、memcpyを割り当てに置き換えることができます。2番目のfor()満たされるメモリの量が64ビットの倍数でない場合、ループが存在します。常にそうなることがわかっている場合は、そのループをドロップするだけです。)

于 2008-09-20T18:00:58.257 に答える
10

標準ライブラリ関数はありません。したがって、移植可能なコードを作成している場合は、ループを見ていることになります。

移植性のないコードを書いている場合は、コンパイラ/プラットフォームのドキュメントを確認してください。他の誰かが、何かを提供するプラットフォームの例を提供するかもしれません。

独自の記述方法は、プラットフォーム (または移植可能な場合はプラットフォーム) で dst ポインターが 64 ビット書き込み用に十分に整列されることを呼び出し元が保証することを API で定義できるかどうかによって異なります。64 ビット整数型を持つすべてのプラットフォームでは、malloc は少なくとも適切に整列されたポインターを返します。

非整合に対処する必要がある場合は、moonshadow の回答のようなものが必要です。コンパイラはその memcpy をサイズ 8 でインライン化/アンロールする可能性がある (存在する場合は 32 ビットまたは 64 ビットのアライメントされていない書き込み操作を使用する) ため、コードはかなりうまくいくはずですが、私の推測では、おそらく特別なケースにはなりません。配置されている宛先の関数全体。訂正されたいと思っていますが、訂正されないのではないかと心配しています。

したがって、呼び出し元が常にアーキテクチャに十分なアライメントと 8 バイトの倍数の長さを持つ dst を提供することがわかっている場合は、uint64_t (または 64 ビットの int があなたのコンパイラ) を使用すると、おそらく (約束はありませんが) より高速なコードが得られるでしょう。確かにコードは短くなります。

いずれにせよ、パフォーマンスが気になる場合は、プロファイルを作成してください。速度が十分でない場合は、さらに最適化して再試行してください。それでも十分に高速でない場合は、十分に高速でない CPU の asm バージョンについて質問してください。memcpy/memset は、プラットフォームごとの最適化によってパフォーマンスを大幅に向上させることができます。

于 2008-09-21T00:12:36.617 に答える
7

念のため、以下はmemcpy(..)以下のパターンで使用します。配列を20個の整数で埋めたいとします。

--------------------

First copy one:
N-------------------

Then copy it to the neighbour:
NN------------------

Then copy them to make four:
NNNN----------------

And so on:
NNNNNNNN------------

NNNNNNNNNNNNNNNN----

Then copy enough to fill the array:
NNNNNNNNNNNNNNNNNNNN

これには、のO(lg(num))アプリケーションが必要ですmemcpy(..)

int *memset_int(int *ptr, int value, size_t num) {
    if (num < 1) return ptr;
    memcpy(ptr, &value, sizeof(int));
    size_t start = 1, step = 1;
    for ( ; start + step <= num; start += step, step *= 2)
        memcpy(ptr + start, ptr, sizeof(int) * step);

    if (start < num)
        memcpy(ptr + start, ptr, sizeof(int) * (num - start));
    return ptr;
}

ハードウェアブロックのメモリコピー機能を使用して最適化すると、ループよりも高速になる可能性があると思いましたがmemcpy(..)、-O2と-O3を使用すると、単純なループの方が上記よりも高速であることがわかります。(少なくとも、特定のハードウェアを搭載したWindowsでMinGW GCCを使用します。)-Oスイッチがない場合、400 MBアレイでは、上記のコードは同等のループの約2倍の速度で、私のマシンでは417ミリ秒かかりますが、最適化すると両方とも約300ミリ秒になります。これは、バイトとほぼ同じナノ秒数を要し、クロックサイクルは約ナノ秒であることを意味します。したがって、私のマシンにハードウェアブロックメモリコピー機能がないか、memcpy(..)実装がそれを利用していません。

于 2012-01-11T13:50:12.457 に答える
6

OSのドキュメントでローカルバージョンを確認してから、ループの使用を検討してください。

コンパイラはおそらく、特定のアーキテクチャでのメモリアクセスの最適化について、あなたよりもよく知っているので、作業を任せてください。

それをライブラリとしてラップし、コンパイラが許可するすべての速度向上最適化を使用してコンパイルします。

于 2008-09-20T18:03:52.150 に答える
5

wmemset(3)memsetのワイド(16ビット)バージョンです。ループなしで、Cで取得するのに最も近いと思います。

于 2008-09-20T17:54:23.593 に答える
2

x86コンパイラをターゲットにしているだけの場合は、次のようなものを試すことができます(VC ++の例)。

inline void memset32(void *buf, uint32_t n, int32_t c)
{
  __asm {
  mov ecx, n
  mov eax, c
  mov edi, buf
  rep stosd
  }
}

それ以外の場合は、単純なループを作成し、オプティマイザーが何をしているのかを信頼して、次のようにします。

for(uint32_t i = 0;i < n;i++)
{
  ((int_32 *)buf)[i] = c;
}

複雑な可能性を持たせると、コードを最適化するのが簡単になるよりも遅くなり、保守が難しくなることは言うまでもありません。

于 2013-03-04T20:18:51.533 に答える
1

他の誰かが提案したように、コンパイラにこれを最適化させる必要があります。ほとんどの場合、そのループはごくわずかです。

ただし、これが特別な状況であり、プラットフォーム固有であることを気にせず、本当にループを取り除く必要がある場合は、アセンブリブロックでこれを行うことができます。

//pseudo code
asm
{
    rep stosq ...
}

あなたはおそらく詳細のためにstosqアセンブリコマンドをグーグルすることができます。数行を超えるコードであってはなりません。

于 2008-09-20T18:15:49.097 に答える
-1

あなた自身を書いてください。asmでも些細なことです。

于 2008-09-20T18:11:28.857 に答える