2016-05-03 10 views
2

私はwin32からwin64にいくつかのレガシーコードを移植した後、「データの損失の可能性あり」(What's the best strategy to get rid of "warning C4267 possible loss of data"?)という警告を取り除く最良の戦略は何かを議論した後、私は多くのunsigned intsize_tで私のコードに置き換えようとしています。unsigned intをsize_tに変更するとパフォーマンスに影響が出る可能性がありますか?

しかし、私のコードはパフォーマンスの点で非常に重要です(私はDebugで実行することすらできません...遅すぎます)。だから、明らかに代わりsize_tを使用して

Elapsed 2756ms 
Elapsed 2748ms 

:x64のためにコンパイルされた

#include "stdafx.h" 

#include <iostream> 
#include <chrono> 
#include <string> 

template<typename T> void testSpeed() 
{ 
    auto start = std::chrono::steady_clock::now(); 

    T big = 0; 
    for (T i = 0; i != 100000000; ++i) 
     big *= std::rand(); 

    std::cout << "Elapsed " << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() << "ms" << std::endl; 
} 

int main() 
{ 
    testSpeed<size_t>(); 
    testSpeed<unsigned int>(); 

    std::string str; 
    std::getline(std::cin, str); // pause 

    return 0; 
} 

、それは出力:x86用のコンパイル

Elapsed 2185ms 
Elapsed 2157ms 

、それは出力

私は迅速なベンチマークを行いました unsigned intのパフォーマンスに与える影響はわずかです。しかし、それは本当に常にそうです(このようにパフォーマンスをベンチマークすることは難しい)。

unsigned intsize_tに変更すると、CPUのパフォーマンスに影響します(現在は64ビットオブジェクトが32ビットではなく操作されるようになります)。

+4

生成されたアセンブリコードを見て、パフォーマンスを測定してみてください。 –

+0

['std :: size_t'](http://en.cppreference.com/w/cpp/types/size_t)は64ビットだと思いますか?実際、その基底型は実装固有のものであり、標準では指定されていません。 – CoryKramer

+0

@CoryKramerでは、OPが 'std :: size_t'ではなく' size_t'を記述しています –

答えて

4

間違いなく。現代(さらに古い)CPUでは、64ビットの整数演算が32ビット演算と同じくらい高速に実行されます。演算a * b/cのための私のI7の4600uに

例:

(int32_t) * (int32_t)/(int32_t):1.3ナノ秒
​​3210:x64のターゲット用にコンパイル1.3ナノ秒

両試験(あなたと同じ目標)。あなたのコードは、整数(ビッグ整数の配列、キツネの一例)の完全な大きなオブジェクトを管理する場合、キャッシュミスが増加を数える場合

Howether、size_t代わりのunsigned intを使用すると、パフォーマンスに影響を与える可能性があり(大きなデータがキャッシュ容量を超えてもよいです) 。パフォーマンスへの影響をチェックする最も信頼できる方法は、どちらの場合でもアプリをテストすることです。 size_tまたはunsigned intのいずれかにtypedefされた独自の型を使用し、アプリケーションのベンチマークを行います。

+1

64ビット操作では、より多くの命令でREX接頭辞が必要なため、x86-64ではコードサイズがわずかに増加します。これは通常重要ではありません。 Silvermontは 'imul r32、r32'より' imul r64、r64'のレイテンシーとスループットが幾分劣りますが、Atomは64bit乗算ではパフォーマンスが*はるかに悪いです。 shift、add/sub、booleanのような単純な演算では、命令のタイミングに違いはありません。 ([Agner Fogの表](http://agner.org/optimize)を参照)。 「通常の」CPUでは、64ビット整数演算は最高速度です。 –

+1

主なスピードの問題はキャッシュフットプリントであるということは間違いありません。一般的に使用されているデータ構造を調べて、まだ 'uint32_t'を使ってうまくいくかどうか調べる価値があります。しかし、size_tとuint32_tの間の変換については心配しないでください。64ビットへのゼロ拡張は、レジスタの32ビットの半分に書き込むときにx86-64上でフリーで起こります。したがって、コードが符号なしラップアラウンドに依存しない限り、 'size_t'の一時変数を使用するといいです... –

0

データ依存性がない場合、少なくともIntelでは、ALUは2つの32ビット演算を並列に実行できます。 size_tが64ビットの場合、1つの操作しか実行できません。あなたはデータDEP持っているので、あなたの例では

は、何の違いは、ありません

次のようなコードでしかし違いを見ることができました(big自体に依存します。):あなたの場合は

uint32_t a = std::rand(); 
uint32_t b = std::rand(); 
const uint32_t randVal = std::rand(); 

for (int i = 0; i < 10000000; ++i) { 
    a += randVal; 
    b += randVal; 
} 

スイッチabuint64_tの場合、一度に1つの操作しか実行できないため、ループの実行速度が遅くなる可能性があります。

ALUは16ビット整数と並列に4演算を行うことはできません。また、8ビット演算を8つ行うこともできません。 32ビットデータの場合は2回、64ビットデータの場合は1回です。

注:これは、生成されたマシンコードには表示されません。この並列化はCPUで行われます。

編集:Andrei Alexandrescuのトークのthis partも参照してください。

+0

このコード(Core i7)をテストしたとき、64ビットと32ビットのデータには大きな違いはありません。 – jpo38

+0

@ jpo38ループは 'uint64_t'でもベクトル化できます。この結果、操作は並行して実行されます。独立変数を増やすと、違いが生じることがあります。 'c、d、e、... 'のように。たぶんそれらの20のような何かをしようとし、何が起こるかを参照してください。いずれにしても、コンパイラオプティマイザを上回ることができない場合でも、Web上のALUアーキテクチャを参照できます。インテルのALUは実際に2つの32ビット操作を同時に実行できます。 –

+0

私はALUがそれを行うことができると信じています。しかし、私は再び変数と余分な乗算演算をテストし、オブジェクトサイズがどのようなものでも違いは見られませんでした。 – jpo38

関連する問題