これらの操作のパフォーマンスに影響を与える要因は何ですか? おそらくコンパイラとコンパイラのバージョンです。しかし、オペレーティングシステムまたはCPUのメーカー/モデルもこれに影響しますか?
主にプロセッサ アーキテクチャ (およびモデル - このセクションでプロセッサ アーキテクチャについて言及しているモデルを参照してください)。コンパイラは多少の影響を与える可能性がありますが、ほとんどのコンパイラはこれにかなりうまく対応しているため、プロセッサ アーキテクチャはコンパイラよりも大きな影響を与えます。
オペレーティングシステムはまったく影響を与えません(「OSを変更する場合、コンパイラの動作を変更する別のタイプのコンパイラを使用する必要がある」場合を除きますが、それはおそらく小さな影響です)。
通常の 32 ビット システムは最新の CPU の 64 ビット レジスタを使用しますか?
これは不可能です。システムが 32 ビット モードの場合、システムは 32 ビット システムとして動作し、システムが実際に「真の 32 ビット システム」であるかのように、レジスタの余分な 32 ビットは完全に見えなくなります。 .
32 ビットでエミュレートすると特に遅くなる操作はどれですか? または、減速がほとんどないのはどれですか?
加算と減算は、2 つの演算のシーケンスで実行する必要があり、2 番目の演算では最初の演算が完了している必要があるため、さらに悪化します。これは、コンパイラが独立したデータに対して 2 つの加算演算を生成するだけの場合には当てはまりません。
入力パラメーターが実際に 64 ビットの場合、乗算はさらに悪化します。たとえば、2^35 * 83 は 2^31 * 2^31 よりも悪化します。これは、プロセッサが 32 x 32 ビットの乗算を 64 ビットの結果にかなりうまく生成できるためです (約 5 ~ 10 クロックサイクル)。ただし、64 x 64 ビットの乗算にはかなりの追加コードが必要なため、時間がかかります。
割り算は掛け算と似た問題ですが、ここでは一方に 64 ビットの入力を取り、それを 32 ビットの値で割り、32 ビットの値を出力しても問題ありません。これがいつ機能するかを予測するのは難しいため、64 ビットの除算はほぼ常に低速です。
また、データは 2 倍のキャッシュ スペースを必要とするため、結果に影響を与える可能性があります。同様の結果として、操作するデータが 2 倍になるため、一般的な割り当てとデータの受け渡しには、最小で 2 倍の時間がかかります。
コンパイラは、より多くのレジスタを使用する必要もあります。
32 ビット システムで int64_t/uint64_t を使用するための既存のベンチマーク結果はありますか?
おそらくですが、私は何も知りません。たとえあったとしても、操作の組み合わせは操作の速度にとって非常に重要であるため、あなたにとってはある程度意味があるだけです。
パフォーマンスがアプリケーションの重要な部分である場合は、コード (またはその代表的な部分) をベンチマークしてください。Benchmark X の結果が 5%、25%、または 103% 遅いかどうかは問題ではありません。同じ状況下で、コードの速度がまったく異なる場合、または速度がまったく異なる場合です。
このパフォーマンスへの影響について自分の経験を持っている人はいますか?
64 ビット アーキテクチャ用に 64 ビット整数を使用するコードを再コンパイルしたところ、パフォーマンスが大幅に改善されたことがわかりました。一部のコードでは 25% も改善されています。
OS を同じ OS の 64 ビット バージョンに変更すると、おそらく役立つでしょうか?
編集:
私はこれらの種類の違いを見つけるのが好きなので、少しコードを書き、いくつかの基本的なテンプレートを使用しました (まだそのビットを学習しています - テンプレートは私の最もホットなトピックではありません、私は言わなければなりません - 教えてくださいビットフィドルとポインター演算、そして私は(通常)それを正しく理解します... )
いくつかの一般的な関数を複製しようとして、私が書いたコードは次のとおりです。
#include <iostream>
#include <cstdint>
#include <ctime>
using namespace std;
static __inline__ uint64_t rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
}
template<typename T>
static T add_numbers(const T *v, const int size)
{
T sum = 0;
for(int i = 0; i < size; i++)
sum += v[i];
return sum;
}
template<typename T, const int size>
static T add_matrix(const T v[size][size])
{
T sum[size] = {};
for(int i = 0; i < size; i++)
{
for(int j = 0; j < size; j++)
sum[i] += v[i][j];
}
T tsum=0;
for(int i = 0; i < size; i++)
tsum += sum[i];
return tsum;
}
template<typename T>
static T add_mul_numbers(const T *v, const T mul, const int size)
{
T sum = 0;
for(int i = 0; i < size; i++)
sum += v[i] * mul;
return sum;
}
template<typename T>
static T add_div_numbers(const T *v, const T mul, const int size)
{
T sum = 0;
for(int i = 0; i < size; i++)
sum += v[i] / mul;
return sum;
}
template<typename T>
void fill_array(T *v, const int size)
{
for(int i = 0; i < size; i++)
v[i] = i;
}
template<typename T, const int size>
void fill_array(T v[size][size])
{
for(int i = 0; i < size; i++)
for(int j = 0; j < size; j++)
v[i][j] = i + size * j;
}
uint32_t bench_add_numbers(const uint32_t v[], const int size)
{
uint32_t res = add_numbers(v, size);
return res;
}
uint64_t bench_add_numbers(const uint64_t v[], const int size)
{
uint64_t res = add_numbers(v, size);
return res;
}
uint32_t bench_add_mul_numbers(const uint32_t v[], const int size)
{
const uint32_t c = 7;
uint32_t res = add_mul_numbers(v, c, size);
return res;
}
uint64_t bench_add_mul_numbers(const uint64_t v[], const int size)
{
const uint64_t c = 7;
uint64_t res = add_mul_numbers(v, c, size);
return res;
}
uint32_t bench_add_div_numbers(const uint32_t v[], const int size)
{
const uint32_t c = 7;
uint32_t res = add_div_numbers(v, c, size);
return res;
}
uint64_t bench_add_div_numbers(const uint64_t v[], const int size)
{
const uint64_t c = 7;
uint64_t res = add_div_numbers(v, c, size);
return res;
}
template<const int size>
uint32_t bench_matrix(const uint32_t v[size][size])
{
uint32_t res = add_matrix(v);
return res;
}
template<const int size>
uint64_t bench_matrix(const uint64_t v[size][size])
{
uint64_t res = add_matrix(v);
return res;
}
template<typename T>
void runbench(T (*func)(const T *v, const int size), const char *name, T *v, const int size)
{
fill_array(v, size);
uint64_t long t = rdtsc();
T res = func(v, size);
t = rdtsc() - t;
cout << "result = " << res << endl;
cout << name << " time in clocks " << dec << t << endl;
}
template<typename T, const int size>
void runbench2(T (*func)(const T v[size][size]), const char *name, T v[size][size])
{
fill_array(v);
uint64_t long t = rdtsc();
T res = func(v);
t = rdtsc() - t;
cout << "result = " << res << endl;
cout << name << " time in clocks " << dec << t << endl;
}
int main()
{
// spin up CPU to full speed...
time_t t = time(NULL);
while(t == time(NULL)) ;
const int vsize=10000;
uint32_t v32[vsize];
uint64_t v64[vsize];
uint32_t m32[100][100];
uint64_t m64[100][100];
runbench(bench_add_numbers, "Add 32", v32, vsize);
runbench(bench_add_numbers, "Add 64", v64, vsize);
runbench(bench_add_mul_numbers, "Add Mul 32", v32, vsize);
runbench(bench_add_mul_numbers, "Add Mul 64", v64, vsize);
runbench(bench_add_div_numbers, "Add Div 32", v32, vsize);
runbench(bench_add_div_numbers, "Add Div 64", v64, vsize);
runbench2(bench_matrix, "Matrix 32", m32);
runbench2(bench_matrix, "Matrix 64", m64);
}
以下でコンパイル:
g++ -Wall -m32 -O3 -o 32vs64 32vs64.cpp -std=c++0x
結果は次のとおりです。注: 以下の 2016 年の結果を参照してください。これらの結果は、64 ビット モードでの SSE 命令の使用の違いにより、やや楽観的ですが、32 ビット モードでの SSE の使用はありません。
result = 49995000
Add 32 time in clocks 20784
result = 49995000
Add 64 time in clocks 30358
result = 349965000
Add Mul 32 time in clocks 30182
result = 349965000
Add Mul 64 time in clocks 79081
result = 7137858
Add Div 32 time in clocks 60167
result = 7137858
Add Div 64 time in clocks 457116
result = 49995000
Matrix 32 time in clocks 22831
result = 49995000
Matrix 64 time in clocks 23823
ご覧のとおり、足し算と掛け算はそれほど悪くありません。分割は本当に悪くなります。興味深いことに、行列の追加はまったく大きな違いはありません。
そして、64ビットの方が速いですか?あなたの何人かが尋ねるのを聞きます:同じコンパイラオプションを使用すると、-m32の代わりに-m64だけです-うん、はるかに高速です:
result = 49995000
Add 32 time in clocks 8366
result = 49995000
Add 64 time in clocks 16188
result = 349965000
Add Mul 32 time in clocks 15943
result = 349965000
Add Mul 64 time in clocks 35828
result = 7137858
Add Div 32 time in clocks 50176
result = 7137858
Add Div 64 time in clocks 50472
result = 49995000
Matrix 32 time in clocks 12294
result = 49995000
Matrix 64 time in clocks 14733
2016 年の編集、更新: SSE の有無にかかわらず、コンパイラの 32 ビット モードと 64 ビット モードでの 4 つのバリアント。
私は通常、最近の通常のコンパイラとして clang++ を使用しています。g++ でコンパイルしてみました (ただし、マシンを更新したため、上記とは異なるバージョンであり、CPU も異なります)。g++ は no-sse バージョンを 64 ビットでコンパイルできなかったため、その意味がわかりませんでした。(g++ とにかく同様の結果が得られます)
短い表として:
Test name | no-sse 32 | no-sse 64 | sse 32 | sse 64 |
----------------------------------------------------------
Add uint32_t | 20837 | 10221 | 3701 | 3017 |
----------------------------------------------------------
Add uint64_t | 18633 | 11270 | 9328 | 9180 |
----------------------------------------------------------
Add Mul 32 | 26785 | 18342 | 11510 | 11562 |
----------------------------------------------------------
Add Mul 64 | 44701 | 17693 | 29213 | 16159 |
----------------------------------------------------------
Add Div 32 | 44570 | 47695 | 17713 | 17523 |
----------------------------------------------------------
Add Div 64 | 405258 | 52875 | 405150 | 47043 |
----------------------------------------------------------
Matrix 32 | 41470 | 15811 | 21542 | 8622 |
----------------------------------------------------------
Matrix 64 | 22184 | 15168 | 13757 | 12448 |
コンパイル オプション付きの完全な結果。
$ clang++ -m32 -mno-sse 32vs64.cpp --std=c++11 -O2
$ ./a.out
result = 49995000
Add 32 time in clocks 20837
result = 49995000
Add 64 time in clocks 18633
result = 349965000
Add Mul 32 time in clocks 26785
result = 349965000
Add Mul 64 time in clocks 44701
result = 7137858
Add Div 32 time in clocks 44570
result = 7137858
Add Div 64 time in clocks 405258
result = 49995000
Matrix 32 time in clocks 41470
result = 49995000
Matrix 64 time in clocks 22184
$ clang++ -m32 -msse 32vs64.cpp --std=c++11 -O2
$ ./a.out
result = 49995000
Add 32 time in clocks 3701
result = 49995000
Add 64 time in clocks 9328
result = 349965000
Add Mul 32 time in clocks 11510
result = 349965000
Add Mul 64 time in clocks 29213
result = 7137858
Add Div 32 time in clocks 17713
result = 7137858
Add Div 64 time in clocks 405150
result = 49995000
Matrix 32 time in clocks 21542
result = 49995000
Matrix 64 time in clocks 13757
$ clang++ -m64 -msse 32vs64.cpp --std=c++11 -O2
$ ./a.out
result = 49995000
Add 32 time in clocks 3017
result = 49995000
Add 64 time in clocks 9180
result = 349965000
Add Mul 32 time in clocks 11562
result = 349965000
Add Mul 64 time in clocks 16159
result = 7137858
Add Div 32 time in clocks 17523
result = 7137858
Add Div 64 time in clocks 47043
result = 49995000
Matrix 32 time in clocks 8622
result = 49995000
Matrix 64 time in clocks 12448
$ clang++ -m64 -mno-sse 32vs64.cpp --std=c++11 -O2
$ ./a.out
result = 49995000
Add 32 time in clocks 10221
result = 49995000
Add 64 time in clocks 11270
result = 349965000
Add Mul 32 time in clocks 18342
result = 349965000
Add Mul 64 time in clocks 17693
result = 7137858
Add Div 32 time in clocks 47695
result = 7137858
Add Div 64 time in clocks 52875
result = 49995000
Matrix 32 time in clocks 15811
result = 49995000
Matrix 64 time in clocks 15168