最新のハードウェアでの浮動小数点と整数の計算


100

私はC ++でパフォーマンスが重要な作業を行っており、現在「本質的に浮動小数点」である問題のために「より速い」ため、整数計算を使用しています。これは多くの迷惑な問題を引き起こし、多くの迷惑なコードを追加します。

浮動小数点の計算が386日ほどで非常に遅くなったことを読んだことを覚えています(IIRC)が、オプションのコプロセッサーがあったと思います。しかし、今日では指数関数的に複雑で強力なCPUを使用しているため、浮動小数点や整数の計算を行っても、「速度」に違いはありませんか?特に、実際の計算時間は、パイプラインのストールを引き起こしたり、メインメモリから何かをフェッチしたりするのに比べて短いのですか?

正しい答えはターゲットハードウェアでベンチマークすることですが、これをテストするための良い方法は何でしょうか?2つの小さなC ++プログラムを作成して、その実行時間とLinuxの「時間」を比較しましたが、実際の実行時間は変動しすぎます(仮想サーバーで実行しているのに役立ちません)。終日何百ものベンチマークの実行、グラフの作成などに費やすのではなく、相対速度の妥当なテストを行うために何かできることはありますか?アイデアや考えはありますか?私は完全に間違っていますか?

私が使用したプログラムは次のとおりですが、まったく同じではありません。

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{
    int accum = 0;

    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += rand( ) % 365;
    }
    std::cout << accum << std::endl;

    return 0;
}

プログラム2:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{

    float accum = 0;
    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += (float)( rand( ) % 365 );
    }
    std::cout << accum << std::endl;

    return 0;
}

前もって感謝します!

編集:私が気にしているプラ​​ットフォームは、デスクトップLinuxおよびWindowsマシンで実行されている通常のx86またはx86-64です。

編集2(下のコメントから貼り付け):現在、広範なコードベースがあります。「整数計算が速いのでfloatを使用してはならない」という一般化に本当に直面しました。この一般化された仮定を否定する方法(これが真実である場合)を探しています。すべての作業を行い、後でプロファイリングすることなくして、正確な結果を予測することは不可能であることを私は理解しています。

とにかく、あなたのすべての優れた答えと助けに感謝します。他に何でも追加してください:)。


8
テストとして今持っているものは取るに足らないものです。また、アセンブリにはほとんど違いがないでしょう(たとえば、にaddl置き換えられfaddます)。実際に適切な測定を行う唯一の方法は、実際のプログラムのコア部分を取得し、そのさまざまなバージョンをプロファイルすることです。残念ながら、これは膨大な労力を費やさないとかなり難しい場合があります。おそらく、ターゲットハードウェアとコンパイラを教えていただければ、少なくとも既存のエクスペリエンスを提供できるようにfixed_pointなるでしょう。
GManNickG 2010年

1
専用の浮動小数点ハードウェアを持たないアーキテクチャはまだたくさんあります-気になるシステムを説明するいくつかのタグは、より良い答えを得るのに役立ちます。
カールノルム2010年

3
私のHTC Hero(android)のハードウェアにはFPUがありませんが、Google NexusOne(android)のハードウェアにはあります。あなたの目標は何ですか?デスクトップ/サーバーPC?ネットブック(可能なarm + linux)?電話?
SteelBytes 2010年

5
x86で高速FPが必要な場合は、最適化とSSEコード生成を使用してコンパイルしてみてください。SSE(どのバージョンでも)は、1つのサイクルで少なくとも浮動小数点の加算、減算、乗算を実行できます。除算、MOD、およびそれ以上の機能は常に遅くなります。またfloat、速度が向上しますが、通常doubleは向上しません。
マイクD.

1
固定小数点整数は、結果がオーバーフローしないように複数の整数演算を使用してFPを近似します。これは、ほとんどの場合、最新のデスクトップCPUにある非常に優れたFPUを使用するよりも遅くなります。たとえば、固定小数点mp3デコーダーであるMADはlibmpg123よりも低速であり、固定小数点デコーダーの品質は優れていますが、libmpg123の丸め誤差はまだ少なくなっています。 wezm.net/technical/2008/04/mp3-decoder-libraries-PPC G5のベンチマークと比較。
Peter Cordes

回答:


35

悲しいかな、私はあなたに「それは依存する」答えを与えることができるだけです...

私の経験から、パフォーマンスには多くの変数があります。特に、整数演算と浮動小数点演算の間です。プロセッサごとに「パイプライン」の長さが異なるため、(x86などの同じファミリ内であっても)プロセッサごとに大きく異なります。また、一部の操作は一般に非常に単純であり(加算など)、プロセッサーを経由するルートが高速化され、他の操作(除算など)にははるかに長い時間がかかります。

もう1つの大きな変数は、データが存在する場所です。追加する値が数個しかない場合は、すべてのデータをキャッシュに常駐させて、CPUにすばやく送信できます。すでにキャッシュにデータがある非常に遅い浮動小数点演算は、整数をシステムメモリからコピーする必要がある整数演算より何倍も高速になります。

パフォーマンスが重要なアプリケーションに取り組んでいるため、この質問をしていると思います。x86アーキテクチャー向けに開発していて、追加のパフォーマンスが必要な場合は、SSE拡張機能の使用を検討することをお勧めします。これにより、同じ精度で複数のデータを同時に処理できるほか、SSE演算用に別個の*バンクのレジスタがあるため、単精度浮動小数点演算が大幅に高速化されます。(2つ目の例では、 "double"ではなく "float"を使用していることに気づき、単精度の数学を使用していると思います)。

*注意:古いMMX命令を使用すると、実際にはプログラムの速度が低下します。これらの古い命令は、実際にはFPUと同じレジスタを使用しており、FPUとMMXの両方を同時に使用することができないためです。


8
また、一部のプロセッサでは、FP演算は整数演算よりも高速になる場合があります。AlphaプロセッサにはFP除算命令がありますが、整数除算命令はありませんでした。そのため、整数除算はソフトウェアで実行する必要がありました。
Gabe 2010年

SSExは倍精度演算も高速化しますか?申し訳ありませんが、SSEにはあまり詳しくありません
Johannes Schaub-litb

1
@ JohannesSchaub-litb:SSE2(x86-64のベースライン)にdouble精度FP がパックされています。doubleレジスタごとに64ビットsが2つしかないfloatため、速度が向上する可能性は、十分にベクトル化するコードよりも小さくなります。x86-64でのスカラーfloatおよびdoubleXMMレジスターの使用long double。従来のx87はにのみ使用されます。(つまり@ Dan:いいえ、x86-64の通常のFPUはSSEユニットであるため、MMXレジスタは通常のFPUレジスタと競合しません。整数SIMDを実行できる場合xmm0..15、8ではなく16バイトが必要であるため、MMXは無意味です。バイトmm0..7、および最新のCPUは、SSEスループットよりもMMXが劣ります。)
Peter Cordes

1
ただし、MMXとSSE * / AVX2の整数命令は同じ実行ユニットで競合するため、両方を同時に使用してもほとんど役に立ちません。幅の広いXMM / YMMバージョンを使用するだけで、より多くの作業を実行できます。SIMD整数とFPを同時に使用すると、同じレジスタで競合しますが、x86-64には16個あります。ただし、合計スループットの制限により、整数実行ユニットとFP実行ユニットを並行して使用することで、2倍の処理を行うことはできません。
Peter Cordes、

49

たとえば(数値が小さいほど速くなります)、

64ビットIntel Xeon X5550 @ 2.67GHz、gcc 4.1.2 -O3

short add/sub: 1.005460 [0]
short mul/div: 3.926543 [0]
long add/sub: 0.000000 [0]
long mul/div: 7.378581 [0]
long long add/sub: 0.000000 [0]
long long mul/div: 7.378593 [0]
float add/sub: 0.993583 [0]
float mul/div: 1.821565 [0]
double add/sub: 0.993884 [0]
double mul/div: 1.988664 [0]

32ビットデュアルコアAMD Opteron(tm)プロセッサー265 @ 1.81 GHz、gcc 3.4.6 -O3

short add/sub: 0.553863 [0]
short mul/div: 12.509163 [0]
long add/sub: 0.556912 [0]
long mul/div: 12.748019 [0]
long long add/sub: 5.298999 [0]
long long mul/div: 20.461186 [0]
float add/sub: 2.688253 [0]
float mul/div: 4.683886 [0]
double add/sub: 2.700834 [0]
double mul/div: 4.646755 [0]

ダンは指摘します(パイプライン化されたデザインで、それ自体に誤解を招くことができます)クロック周波数を正規化するためにも一度、結果はCPUアーキテクチャに基づいて乱暴に変化します(個別のALU / FPUのパフォーマンスなど実際のALU / FPUの数利用できるあたり並列に実行できる独立した操作の数に影響を与えるスーパースカラーデザインのコア-以下のすべての操作は順次依存するため、後者の要素は以下のコードでは実行されません。)

貧乏人のFPU / ALU運用ベンチマーク:

#include <stdio.h>
#ifdef _WIN32
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <time.h>
#include <cstdlib>

double
mygettime(void) {
# ifdef _WIN32
  struct _timeb tb;
  _ftime(&tb);
  return (double)tb.time + (0.001 * (double)tb.millitm);
# else
  struct timeval tv;
  if(gettimeofday(&tv, 0) < 0) {
    perror("oops");
  }
  return (double)tv.tv_sec + (0.000001 * (double)tv.tv_usec);
# endif
}

template< typename Type >
void my_test(const char* name) {
  Type v  = 0;
  // Do not use constants or repeating values
  //  to avoid loop unroll optimizations.
  // All values >0 to avoid division by 0
  // Perform ten ops/iteration to reduce
  //  impact of ++i below on measurements
  Type v0 = (Type)(rand() % 256)/16 + 1;
  Type v1 = (Type)(rand() % 256)/16 + 1;
  Type v2 = (Type)(rand() % 256)/16 + 1;
  Type v3 = (Type)(rand() % 256)/16 + 1;
  Type v4 = (Type)(rand() % 256)/16 + 1;
  Type v5 = (Type)(rand() % 256)/16 + 1;
  Type v6 = (Type)(rand() % 256)/16 + 1;
  Type v7 = (Type)(rand() % 256)/16 + 1;
  Type v8 = (Type)(rand() % 256)/16 + 1;
  Type v9 = (Type)(rand() % 256)/16 + 1;

  double t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v += v0;
    v -= v1;
    v += v2;
    v -= v3;
    v += v4;
    v -= v5;
    v += v6;
    v -= v7;
    v += v8;
    v -= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s add/sub: %f [%d]\n", name, mygettime() - t1, (int)v&1);
  t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v /= v0;
    v *= v1;
    v /= v2;
    v *= v3;
    v /= v4;
    v *= v5;
    v /= v6;
    v *= v7;
    v /= v8;
    v *= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s mul/div: %f [%d]\n", name, mygettime() - t1, (int)v&1);
}

int main() {
  my_test< short >("short");
  my_test< long >("long");
  my_test< long long >("long long");
  my_test< float >("float");
  my_test< double >("double");

  return 0;
}

8
なぜmultとdivを混ぜたのですか?MULTがDIVよりもはるかに速い(または予想される?)場合は、興味深いことではありませんか?
Kyss Tao

13
整数と浮動小数点の両方の場合、乗算は除算よりもはるかに高速です。除算のパフォーマンスは、数値のサイズにも依存します。通常、除算は約15倍遅いと思います。
ソガルタール

4
pastebin.com/Kx8WGUfg私はあなたのベンチマークを取り、各操作を独自のループに分離しvolatile、確認のために追加しました。Win64では、FPUは使用されておらず、MSVCはそのコードを生成しないためmulssdivssXMM命令を使用してコンパイルします。これは、Win32のFPUより25倍高速です。テストマシンはCore i5 M 520 @ 2.40GHz
James Dunne

4
@JamesDunneは注意が必要です。fpops vは0または+/- infのいずれかに非常に速く到達し、特定のfpu実装によって(理論的には)特殊なケース/ファストパスとして扱われる場合とされない場合があります。
vladr 2013年

3
すべての操作が同じアキュムレータ(v)で行われるため、この「ベンチマーク」には、順不同実行のデータ並列処理がありません。最近のIntelの設計では、除算はパイプライン処理されていません(divss/にdivpsは10〜14サイクルのレイテンシがあり、同じ相互スループットがあります)。 mulssただし、レイテンシは5サイクルですが、サイクルごとに1つ発行できます。(または、ポート0とポート1の両方にFMAの乗数があるため、Haswellではサイクルごとに2つ)。
Peter Cordes

23

固定小数点演算と浮動小数点演算の実際の速度には大きな違いがあるようですが、ALUとFPUの理論的なベストケースのスループットは完全に無関係です。代わりに、アーキテクチャー上の整数および浮動小数点レジスター(レジスター名ではなく、実際のレジスター)の数で、他の方法では計算(ループ制御など)で使用されないもの、キャッシュラインに収まる各タイプの要素の数、最適化は、整数演算と浮動小数点演算の異なるセマンティクスを考慮して可能です。これらの効果が支配的です。ここではアルゴリズムのデータ依存性が重要な役割を果たすため、一般的な比較では問題のパフォーマンスギャップを予測できません。

たとえば、整数の加算は可換的であるため、ベンチマークに使用したようなループがコンパイラーで検出された場合(ランダムデータが事前に準備されており、結果が不明瞭にならないと想定)、ループを展開して部分的な合計を計算できます。依存関係がなく、ループが終了したときにそれらを追加します。しかし、浮動小数点の場合、コンパイラーは要求したのと同じ順序で操作を実行する必要があります(そこにシーケンスポイントがあるため、コンパイラーは同じ結果を保証する必要があります。これにより、並べ替えができなくなります)。前のものの結果。

同時に、より多くの整数オペランドをキャッシュに収める可能性があります。したがって、固定小数点バージョンは、FPUの理論的スループットが高いマシンでも、浮動小数点バージョンよりも桁違いに優れている場合があります。


4
+1は、展開された定数整数演算のために単純なベンチマークが0時間ループを生成する方法を示すために使用します。さらに、結果が実際に使用されない場合、コンパイラーはループ(整数またはFP)を完全に破棄できます。
vladr 2010年

結論としては、ループ変数を引数として持つ関数を呼び出さなければなりません。関数が何もせず、呼び出しを無視できることをコンパイラが認識できないと思うので。呼び出しのオーバーヘッドがあるため、time ==(float time-integer time)の違いのみが重要になります。
GameAlchemist 2013

@GameAlchemist:多くのコンパイラは、インライン化の副作用として、空の関数の呼び出しを排除します。それを防ぐ努力をしなければなりません。
Ben Voigt 2014

OPは、FPがより自然に適合するものに整数を使用することについて話しているように聞こえたため、FPコードと同じ結果を得るには、より多くの整数コードが必要になります。この場合は、FPを使用してください。たとえば、FPUを備えたハードウェア(デスクトップCPUなど)では、固定小数点整数MP3デコーダーは、浮動小数点デコーダーよりも低速です(丸め誤差がわずかに多い)。コーデックの固定小数点実装は、主に、FPハードウェアがなく、エミュレートされたFPが遅いストリップダウンされたARM CPUで実行するために存在します。
Peter Cordes、2015

最初のポイントの1つの例:x86-64とAVX-512の場合、GPレジスタは16個しかありませんが、zmmレジスタは32個なので、スカラー浮動小数点演算より高速になる可能性があります
phuclv

18

加算はよりもはるかに高速でrandあるため、プログラムは(特に)役に立たなくなります。

パフォーマンスのホットスポットを特定し、プログラムを段階的に変更する必要があります。最初に解決する必要がある開発環境に問題があるようです。小さな問題セットをPCで実行することは不可能ですか?

一般に、整数演算を使用してFPジョブを試行することは、低速のレシピです。


ええ、浮動小数点バージョンでのrand整数からfloatへの変換。これをテストするより良い方法に関するアイデアはありますか?
maxpenguin 2010年

1
速度のプロファイルを作成しようとしている場合は、POSIX timespec_tまたは類似のものを見てください。ループの開始と終了の時間を記録し、差をとります。次に、randデータ生成をループの外に移動します。アルゴリズムがすべてのデータを配列から取得し、すべてのデータを配列に入れることを確認してください。これにより、実際のアルゴリズムが単独で取得され、セットアップ、malloc、結果の出力、タスクの切り替え以外のすべてが取得され、プロファイリングループから中断されます。
マイクD.

3
@maxpenguin:問題はあなたが何をテストしているかです。Artemはあなたがグラフィックスをやっていると思っていました、カールはあなたがFPではなく組み込みプラットフォームを使っているかどうかを考えました。ベンチマークを一般化したり、「記述」することはできません。ベンチマークは、プログラムが行う実際の作業からサンプリングされます。私が言えることの1つは、プログラムのパフォーマンスが重要な要素に触れた場合、それが何であれ、「本質的に同じ速度」を維持できないことです。
Potatoswatter 2010年

良い点と良い答え。現在、広範なコードベースがあります。「整数計算が速いのでfloatを使用してはならない」という一般化に本当に直面しました。この一般化された仮定を否定する方法(これが真実である場合)を探しています。すべての作業を行って後でプロファイリングするまでは、正確な結果を予測することは不可能だと思います。とにかく、あなたの助けに感謝します。
maxpenguin 2010年

18

TILこれは(かなり)異なります。ここにgnuコンパイラを使用した結果があります(私はマシンでコンパイルして確認しましたが、xenialのgnu g ++ 5.4は、正確にlinaroの4.6.3よりもはるかに高速です)

Intel i7 4700MQ xenial

short add: 0.822491
short sub: 0.832757
short mul: 1.007533
short div: 3.459642
long add: 0.824088
long sub: 0.867495
long mul: 1.017164
long div: 5.662498
long long add: 0.873705
long long sub: 0.873177
long long mul: 1.019648
long long div: 5.657374
float add: 1.137084
float sub: 1.140690
float mul: 1.410767
float div: 2.093982
double add: 1.139156
double sub: 1.146221
double mul: 1.405541
double div: 2.093173

Intel i3 2370Mでも同様の結果が得られます

short add: 1.369983
short sub: 1.235122
short mul: 1.345993
short div: 4.198790
long add: 1.224552
long sub: 1.223314
long mul: 1.346309
long div: 7.275912
long long add: 1.235526
long long sub: 1.223865
long long mul: 1.346409
long long div: 7.271491
float add: 1.507352
float sub: 1.506573
float mul: 2.006751
float div: 2.762262
double add: 1.507561
double sub: 1.506817
double mul: 1.843164
double div: 2.877484

Intel(R)Celeron(R)2955U(xenialを実行するAcer C720 Chromebook)

short add: 1.999639
short sub: 1.919501
short mul: 2.292759
short div: 7.801453
long add: 1.987842
long sub: 1.933746
long mul: 2.292715
long div: 12.797286
long long add: 1.920429
long long sub: 1.987339
long long mul: 2.292952
long long div: 12.795385
float add: 2.580141
float sub: 2.579344
float mul: 3.152459
float div: 4.716983
double add: 2.579279
double sub: 2.579290
double mul: 3.152649
double div: 4.691226

DigitalOcean 1GBドロップレットIntel(R)Xeon(R)CPU E5-2630L v2(trustyを実行)

short add: 1.094323
short sub: 1.095886
short mul: 1.356369
short div: 4.256722
long add: 1.111328
long sub: 1.079420
long mul: 1.356105
long div: 7.422517
long long add: 1.057854
long long sub: 1.099414
long long mul: 1.368913
long long div: 7.424180
float add: 1.516550
float sub: 1.544005
float mul: 1.879592
float div: 2.798318
double add: 1.534624
double sub: 1.533405
double mul: 1.866442
double div: 2.777649

AMD Opteron(tm)プロセッサー4122(正確)

short add: 3.396932
short sub: 3.530665
short mul: 3.524118
short div: 15.226630
long add: 3.522978
long sub: 3.439746
long mul: 5.051004
long div: 15.125845
long long add: 4.008773
long long sub: 4.138124
long long mul: 5.090263
long long div: 14.769520
float add: 6.357209
float sub: 6.393084
float mul: 6.303037
float div: 17.541792
double add: 6.415921
double sub: 6.342832
double mul: 6.321899
double div: 15.362536

これはからのコードを使用していますhttp://pastebin.com/Kx8WGUfgとしてbenchmark-pc.c

g++ -fpermissive -O3 -o benchmark-pc benchmark-pc.c

私は複数のパスを実行しましたが、これは一般的な数が同じである場合のようです。

注目すべき例外の1つは、ALUマルチとFPUマルチのようです。足し算と引き算はごくわずかに異なっているように見えます。

以下は、上記のグラフ形式です(フルサイズの場合はクリックしてください。下の方が高速で望ましいです)。

上記データのグラフ

@Peter Cordesに対応するための更新

https://gist.github.com/Lewiscowles1986/90191c59c9aedf3d08bf0b129065cccc

i7 4700MQ Linux Ubuntu Xenial 64ビット(2018-03-13へのすべてのパッチが適用されています)
    short add: 0.773049
    short sub: 0.789793
    short mul: 0.960152
    short div: 3.273668
      int add: 0.837695
      int sub: 0.804066
      int mul: 0.960840
      int div: 3.281113
     long add: 0.829946
     long sub: 0.829168
     long mul: 0.960717
     long div: 5.363420
long long add: 0.828654
long long sub: 0.805897
long long mul: 0.964164
long long div: 5.359342
    float add: 1.081649
    float sub: 1.080351
    float mul: 1.323401
    float div: 1.984582
   double add: 1.081079
   double sub: 1.082572
   double mul: 1.323857
   double div: 1.968488
AMD Opteron(tm)プロセッサ4122(正確、DreamHost共有ホスティング)
    short add: 1.235603
    short sub: 1.235017
    short mul: 1.280661
    short div: 5.535520
      int add: 1.233110
      int sub: 1.232561
      int mul: 1.280593
      int div: 5.350998
     long add: 1.281022
     long sub: 1.251045
     long mul: 1.834241
     long div: 5.350325
long long add: 1.279738
long long sub: 1.249189
long long mul: 1.841852
long long div: 5.351960
    float add: 2.307852
    float sub: 2.305122
    float mul: 2.298346
    float div: 4.833562
   double add: 2.305454
   double sub: 2.307195
   double mul: 2.302797
   double div: 5.485736
Intel Xeon E5-2630L v2 @ 2.4GHz(Trusty 64ビット、DigitalOcean VPS)
    short add: 1.040745
    short sub: 0.998255
    short mul: 1.240751
    short div: 3.900671
      int add: 1.054430
      int sub: 1.000328
      int mul: 1.250496
      int div: 3.904415
     long add: 0.995786
     long sub: 1.021743
     long mul: 1.335557
     long div: 7.693886
long long add: 1.139643
long long sub: 1.103039
long long mul: 1.409939
long long div: 7.652080
    float add: 1.572640
    float sub: 1.532714
    float mul: 1.864489
    float div: 2.825330
   double add: 1.535827
   double sub: 1.535055
   double mul: 1.881584
   double div: 2.777245

gcc5は、gcc4.6が行わなかったものを自動ベクトル化するのでしょうか?されるbenchmark-pcスループットとレイテンシのいくつかの組み合わせを測定しますか?Haswell(i7 4700MQ)では、整数の乗算はクロックスループットあたり1、3サイクルのレイテンシですが、整数の加算/ 減算はクロックスループットあたり4、1サイクルのレイテンシです(agner.org/optimize)。そのため、おそらく、addとmulが非常に近くなるようにこれらの数値を希釈する多くのループオーバーヘッドがある(長いadd:0.824088とlong mul:1.017164)。(gccのデフォルトでは、ループをアンロールしないようになっていますが、非常に少ない反復回数を完全にアンロールする場合を除きます)。
Peter Cordes

そしてところで、なぜそれがテストしていないintだけで、shortlong?Linux x86-64では、shortは16ビットです(したがって、一部のレジスターが遅くなる場合があります)longlong longはどちらも64ビットタイプです。(多分それはx86-64がまだ32ビットを使用するWindows用に設計されていますlongか?それとも32ビットモード用に設計されています。)Linuxでは、x32 ABIはlong64ビットモードで32ビットなので、ライブラリがインストールされている場合、gcc -mx32ILP32のコンパイラに使用します。または、使用-m32してlong数値を確認します。
Peter Cordes

そして、コンパイラーが何かを自動ベクトル化したかどうかを実際に確認する必要があります。たとえば、のaddps代わりにxmmレジスタを使用してaddss、4つのFPを1つの命令で並列に追加し、スカラーと同じくらい高速addssです。(-march=nativex86-64のSSE2ベースラインだけでなく、CPUがサポートする命令セットを使用できるようにするために使用します)。
Peter Cordes

@cincodenadaは、パフォーマンスを例示するものであるため、15を上にしてグラフを残してください。
MrMesees 2018年

@PeterCordes明日見てみます、あなたの勤勉に感謝します。
MrMesees 2018年

7

考慮すべき2つの点-

最新のハードウェアは、命令をオーバーラップさせ、それらを並行して実行し、ハードウェアを最大限に活用するためにそれらを並べ替えます。また、重要な浮動小数点プログラムは、配列やループカウンターなどへのインデックスを計算するだけの場合でも、重要な整数処理を行う可能性が高いため、低速の浮動小数点命令がある場合でも、ハードウェアの別のビットで実行される可能性があります。整数の一部と重複しています。私のポイントは、浮動小数点命令が整数の命令より遅い場合でも、ハードウェアをより多く利用できるため、プログラム全体がより高速に実行される可能性があるということです。

いつものように、確実にする唯一の方法は、実際のプログラムをプロファイルすることです。

2つ目のポイントは、最近のほとんどのCPUには、複数の浮動小数点値を同時に操作できる浮動小数点用のSIMD命令があるということです。たとえば、4つの浮動小数点を単一のSSEレジスタにロードし、それらに対して4つの乗算をすべて並行して実行できます。SSE命令を使用するようにコードの一部を書き換えることができれば、整数バージョンよりも高速になる可能性があります。Visual c ++には、これを行うためのコンパイラ組み込み関数が用意されています。詳細については、http://msdn.microsoft.com/en-us/library/x5c07e2a(v = VS.80).aspx参照してください。


Win64では、FPU命令はMSVCコンパイラーによって生成されなくなったことに注意してください。浮動小数点は常にそこでSIMD命令を使用しています。これにより、フロップに関してWin32とWin64の間で大きな速度の不一致が生じます。
James Dunne、2013年

5

残りの演算がない場合、浮動小数点バージョンははるかに遅くなります。すべての加算は順次的であるため、CPUは合計を並列化できません。待ち時間は非常に重要です。FPU追加レイテンシは通常3サイクルですが、整数追加は1サイクルです。ただし、残りの演算子の除算器は、最新のCPUでは完全にパイプライン化されていないため、おそらく重要な部分になります。そのため、除算/剰余命令が時間の大部分を消費すると仮定すると、追加レイテンシによる差は小さくなります。


4

1秒あたり数百万回呼び出されるコード(たとえば、グラフィックスアプリケーションで画面に線を引くなど)を記述している場合を除き、整数と浮動小数点の演算がボトルネックになることはほとんどありません。

効率に関する質問への通常の最初のステップは、コードをプロファイリングして、ランタイムが実際に費やされている場所を確認することです。このためのLinuxコマンドはgprofです。

編集:

整数と浮動小数点数を使用して常に線描画アルゴリズムを実装できると思いますが、これを何度も呼び出して、違いがあるかどうかを確認します。

http://en.wikipedia.org/wiki/Bresenham's_algorithm


2
科学的アプリケーションはFPを使用します。FPの唯一の利点は、精度がスケール不変であることです。それは科学表記法のようなものです。数値のスケールがすでにわかっている場合(たとえば、行の長さがピクセル数であること)、FPは不要です。しかし、線を引く前に、それは真実ではありません。
Potatoswatter 2010年

4

今日、整数演算は通常、浮動小数点演算よりも少し高速です。したがって、整数と浮動小数点で同じ演算を使用して計算できる場合は、整数を使用します。しかし、「これは非常に多くの迷惑な問題を引き起こし、多くの迷惑なコードを追加します」と言っています。浮動小数点の代わりに整数演算を使用するため、より多くの演算が必要なように思えます。その場合、浮動小数点はより高速に実行されます。

  • より多くの整数演算が必要になるとすぐに、さらに多くのものが必要になる可能性があるため、わずかな速度の利点は、追加の演算によって食べ尽くされる以上のものです。

  • 浮動小数点コードは単純です。つまり、コードを書くのが高速です。つまり、速度が重要な場合は、コードの最適化により多くの時間を費やすことができます。


ここには多くの野生の推測があり、ハードウェアに存在する二次的影響のいずれも考慮に入れていません。これは、多くの場合、計算時間を支配します。悪い出発点ではありませんが、プロファイリングを介して特定のアプリケーションごとにチェックする必要があり、福音として教えることはできません。
Ben Voigt 2014

3

rand()の代わりに数値に1を加えただけのテストを実行しました。結果(x86-64の場合):

  • 短い:4.260s
  • int:4.020s
  • ロングロング:3.350秒
  • float:7.330s
  • ダブル:7.210s

1
ソース、コンパイルオプション、およびタイミングメソッド?結果に少し驚いています。
GManNickG 2010年

OPと同じループで、「rand()%365」が「1」に置き換えられています。最適化なし。「time」コマンドからのユーザー時間。
dan04

13
「最適化なし」が鍵です。最適化をオフにしてプロファイリングすることはなく、常に「リリース」モードでプロファイリングします。
ディーンハーディング

2
ただし、この場合、最適化をオフにすることでopが強制的に発生し、意図的に行われます。ループは、時間を適切な測定スケールに拡張するためにあります。定数1を使用すると、rand()のコストが削除されます。十分にスマートな最適化コンパイラーは、ループから抜け出す方法なしで1が100,000,000回追加され、単一の演算で100000000が追加されることを確認します。そういうのは目的全体をうまく回っていますね。
スタンロジャース

7
@Stan、変数を揮発性にします。スマートな最適化コンパイラでさえ、複数の操作を尊重する必要があります。
vladr

0

その信頼性の高い「聞いたこと」に基づいて、昔は整数計算は浮動小数点の約20倍から50倍高速でしたが、最近では2倍未満の高速です。


1
(特に、収集された事実に直面して意見が飛び交うように思われる場合は特に)意見以上の内容を提供するこの
記事を

1
@MrMeseesこの答えはそれほど有用ではありませんが、あなたが行ったテストと一致しています。そして、歴史的な雑学もおそらく素晴らしいです。
JonatanÖström2017年

286年代に働いていた人として、私は確認できます。"はい、そうでした!"
David H Parry、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.