6

この関数を最適化するために、C ++でfloat配列の値を乗算する高速な方法はありますか(count4の倍数です)。

void multiply(float* values, float factor, int count)
{
    for(int i=0; i < count; i++)
    {
        *value *= factor;
        value++;
    }
}

ソリューションは、Mac OS XとWindows、Intelと非Intelで機能する必要があります。SSE、ベクトル化、コンパイラー(gccとMSVC)を考えてみてください。

4

7 に答える 7

2

コードをクロスプラットフォームにしたい場合は、プラットフォームに依存しないコードを作成するか、大量の#ifdefsを作成する必要があります。

手動でループを展開して、違いが生じるかどうかを確認しましたか?

于 2010-09-09T11:21:07.793 に答える
2

が 4 の倍数であることがわかっているのでcount、ループを展開できます...

void multiply(float* values, float factor, int count)
{
    count = count >> 2; // count / 4
    for(int i=0; i < count ; i++)
    {
        *value *= factor;
        *(value+1) *= factor;
        *(value+2) *= factor;
        *(value+3) *= factor;
        value += 4;
    }
}
于 2010-09-09T11:24:19.130 に答える
2

免責事項: 明らかに、これは iPhone、iPad、Android、またはそれらの将来の同等物では機能しません。

#include <mmintrin.h>
#include <xmmintrin.h>

__m128 factor4 = _mm_set1_ps(factor);
for (int i=0; i+3 < count; i += 4)
{
   __m128 data = _mm_mul_ps(_mm_loadu_ps(values), factor4);
   _mm_storeu_ps(values, data);
   values += 4;
}
for (int i=(count/4)*4; i < count; i++)
{
   *values *= factor;
   value++;
}
于 2010-09-09T11:29:07.797 に答える
2

OpenMP について考えたことはありますか?

最近のほとんどのコンピューターにはマルチコア CPU が搭載されており、ほぼすべての主要なコンパイラーに OpenMP が組み込まれているようです。ほとんどコストをかけずに速度を上げます。

OpenMP に関するウィキペディアの記事を参照してください。

于 2010-09-09T11:32:44.637 に答える
0

最善の解決策は、単純なままにして、コンパイラーに最適化させることです。GCC は、SSE、SSE2、altivec などについて知っています。コードが複雑すぎると、コンパイラは考えられるすべてのターゲットでコードを最適化できなくなります。

于 2010-09-09T11:35:56.590 に答える
0

おっしゃったように、SIMD 拡張機能を備えたアーキテクチャは数多くありますが、最適化に関してはおそらく SIMD が最善の策です。ただし、それらはすべてプラットフォーム固有であり、言語としての C および C++ は SIMD フレンドリーではありません。

ただし、最初に試す必要があるのは、特定のビルドの SIMD 固有のフラグを有効にすることです。コンパイラーは、SIMD で最適化できるパターンを認識する場合があります。

次に、必要に応じてコンパイラの組み込み関数またはアセンブリを使用して、プラットフォーム固有の SIMD コードを記述します。ただし、最適化されたバージョンがないプラットフォームでは、移植可能な非 SIMD 実装を保持する必要があります。#ifdef■ SIMD をサポートするプラットフォームで SIMD を有効にします。

最後に、少なくとも ARM ではわかりませんが、Intel では不明ですが、整数型と浮動小数点型が小さいほど、単一の SIMD 命令ごとにより多くの並列操作が可能になることに注意してください。

于 2010-09-09T13:27:33.503 に答える
0

大きな違いを生むためにできることはあまりないと思います。OpenMP や SSE を使用すると、少し高速化できるかもしれません。しかし、最新の CPU はすでにかなり高速です。一部のアプリケーションでは、実際にはメモリ帯域幅/レイテンシがボトルネックであり、さらに悪化します。すでに 3 レベルのキャッシュがあり、大幅な遅延を回避するためにスマートなプリフェッチ アルゴリズムが必要です。そのため、メモリ アクセス パターンについても考えるのが理にかなっています。たとえば、そのような amultiplyおよび anを実装し、次のaddように使用する場合:

void multiply(float vec[], float factor, int size)
{
  for (int i=0; i<size; ++i)
    vec[i] *= factor;
}

void add(float vec[], float summand, int size)
{
  for (int i=0; i<size; ++i)
    vec[i] += summand;
}

void foo(float vec[], int size)
{
  multiply(vec,2.f,size);
  add(vec,9.f,size);
}

基本的に、メモリのブロックを 2 回渡しています。ベクターのサイズによっては、L1 キャッシュに収まらない場合があります。この場合、ベクターを 2 回渡すと余分な時間が追加されます。これは明らかに悪いことであり、メモリアクセスを「ローカル」に保つようにしてください。この場合、単一のループ

void foo(float vec[], int size)
{
  for (int i=0; i<size; ++i) {
    vec[i] = vec[i]*2+9;
  }
}

より速くなる可能性があります。経験則として、メモリに線形にアクセスし、「ローカルに」メモリにアクセスしてみてください。つまり、L1 キャッシュに既にあるデータを再利用してみてください。ただのアイデア。

于 2010-09-09T13:46:34.037 に答える