Ответ 1
который влияет на эффективность этих операций? Вероятно, компилятор и версия компилятора; но работает ли операционная система или CPU make/model влияет на это также?
В основном архитектура процессора (и модель - пожалуйста, прочтите модель, где я упоминаю архитектуру процессора в этом разделе). Компилятор может иметь некоторое влияние, но большинство компиляторов очень хорошо справляются с этим, поэтому архитектура процессора будет иметь большее влияние, чем компилятор.
Операционная система не будет иметь никакого влияния (кроме "если вы меняете ОС, вам нужно использовать другой тип компилятора, который изменяет то, что делает компилятор" в некоторых случаях, но это, вероятно, небольшой эффект).
Будет ли обычная 32-разрядная система использовать 64-разрядные регистры современных процессоров?
Это невозможно. Если система находится в 32-битном режиме, она будет действовать как 32-битная система, дополнительные 32-разрядные регистры полностью невидимы, как и было бы, если бы система была фактически "истинной 32-разрядной системой",
какие операции будут особенно медленными при эмулировании на 32-битных? Или что почти не будет замедления?
Сложение и вычитание хуже, поскольку они должны выполняться в последовательности из двух операций, а вторая операция требует завершения первой - это не тот случай, когда компилятор просто производит две операции над независимыми данными.
Mulitplication будет намного хуже, если входные параметры на самом деле являются 64-битными - так что 2 ^ 35 * 83 хуже, чем 2 ^ 31 * 2 ^ 31, например. Это связано с тем, что процессор может производить 32 x 32-битное умножение на 64-битный результат довольно хорошо - около 5-10 тактов. Но для 64-битного 64-битного умножения требуется справедливый бит дополнительного кода, поэтому потребуется больше времени.
Подразделение является аналогичной проблемой для умножения - но здесь нужно взять 64-битный вход с одной стороны, разделить его на 32-битное значение и получить 32-битное значение. Поскольку трудно предсказать, когда это будет работать, 64-разрядное разделение, вероятно, почти всегда медленное.
Данные также будут занимать в два раза больше пространства кеша, что может повлиять на результаты. И как аналогичное следствие, общее назначение и передача данных примерно в два раза больше, так как в два раза больше данных для работы.
Компилятору также потребуется использовать больше регистров.
Есть ли какие-либо существующие результаты тестов для использования int64_t/uint64_t в 32-битных системах?
Возможно, но я ничего не знаю. И даже если есть, это будет только несколько значимым для вас, поскольку сочетание операций является высоко критичным для скорости операций.
Если производительность является важной частью вашего приложения, то проверьте свой код (или часть его части). На самом деле не имеет значения, дает ли Benchmark X 5%, на 25% или 103% более медленные результаты, если ваш код в какой-то степени отличается медленнее или быстрее при тех же обстоятельствах.
У кого-нибудь есть собственный опыт в отношении этого воздействия на производительность?
Я перекомпилировал код, который использует 64-битные целые числа для 64-битной архитектуры, и нашел, что производительность улучшена на некоторую значительную сумму - до 25% на некоторые биты кода.
Изменение ОС на 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
И результаты: Примечание. См. ниже результаты. Эти результаты немного оптимистичны из-за различий в использовании инструкций SSE в 64-битном режиме, но использование SSE в 32-битных режим.
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-битных, я слышал, некоторые из вас спрашивают: Используя одни и те же параметры компилятора, просто -m64 вместо -m32 - yupp, намного быстрее:
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-битном режиме компилятора.
Обычно я использую clang++ в качестве своего обычного компилятора. Я попробовал компиляцию с g++ (но это все равно будет другая версия, чем выше, поскольку я обновил свою машину - и у меня тоже есть другой процессор). Поскольку 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