g ++, парный разряд, оптимизация и большой WTF - PullRequest
2 голосов
/ 03 сентября 2010

ошибка в моем GCC?ошибка в моем коде?оба?

http://files.minthos.com/code/speedtest_doubles_wtf.cpp

Каким-то образом ей удается "оптимизировать" функцию, которая приводит к обнулению массива значений типа double, что занимает 2,6 секунды на моем q6600 вместо 33 мсболее сложная функция требует заполнения массива чем-то несколько осмысленным.

Мне было бы интересно узнать, получат ли другие аналогичные результаты, и если да, то сможет ли кто-нибудь объяснить, что происходит ... А также выяснитьчто вызывает огромную разницу между целочисленной и производительностью с плавающей точкой (особенно при компиляции без оптимизации).

Ответы [ 3 ]

5 голосов
/ 03 сентября 2010

Строка 99:

memcpy(floats, ints, sizeof(floats));

частично инициализирует floats[] эффективно с мусором с плавающей запятой.Остальные остаются нулевыми.Это связано с заменой чисел с плавающей точкой целочисленными растровыми изображениями и последующей интерпретацией их как двойных.Возможно, переполнение и понижение влияют на производительность?Для проверки я изменил начальное число случайных чисел на постоянную 1000 для воспроизводимости и получил следующие результаты:

[wally@zenetfedora Downloads]$ ./speedtest_doubles_wtf.cpp
no optimization
begin: 0.017000
floats: 27757.816000
ints: 28117.604000
floats: 40346.196000
ints: 41094.988000
sum: 7999999.998712
sum2: 67031739228347449344.000000
mild optimization
begin: 0.014000
floats: 68.574000
ints: 68.609000
floats: 147.105000
ints: 820.609000
sum: 8000000.000001
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.014000
floats: 73.588000
ints: 73.623000
floats: 144.105000
ints: 1809.980000
sum: 8000000.000001
sum2: 67031739228347441152.000000
again, now using ffun2()
no optimization
begin: 0.017000
floats: 22720.648000
ints: 23076.134000
floats: 35480.824000
ints: 36229.484000
floats: 46324.080000
sum: 0.000000
sum2: 67031739228347449344.000000
mild optimization
begin: 0.013000
floats: 69.937000
ints: 69.967000
floats: 138.010000
ints: 965.654000
floats: 19096.902000
sum: 0.000000
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.015000
floats: 95.851000
ints: 95.896000
floats: 206.594000
ints: 1699.698000
floats: 29382.348000
sum: 0.000000
sum2: 67031739228347441152.000000

Повторение после замены memcpy на правильное назначение, чтобы преобразование типа могло происходить, должно предотвратить граничные условия с плавающей запятой:

for(int i = 0; i < 16; i++)
{
    ints[i] = rand();
    floats[i]= ints[i];
}

Модифицированная программа, все еще с постоянной 1000 в качестве случайного начального числа, дает следующие результаты:

[wally@zenetfedora Downloads]$ ./speedtest_doubles_wtf.cpp
no optimization
begin: 0.013000
floats: 35814.832000
ints: 36172.180000
floats: 85950.352000
ints: 86691.680000
sum: inf
sum2: 67031739228347449344.000000
mild optimization
begin: 0.013000
floats: 33136.644000
ints: 33136.678000
floats: 51600.436000
ints: 52494.104000
sum: inf
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.013000
floats: 31914.496000
ints: 31914.540000
floats: 48611.204000
ints: 49971.460000
sum: inf
sum2: 67031739228347441152.000000
again, now using ffun2()
no optimization
begin: 0.014000
floats: 40202.956000
ints: 40545.120000
floats: 104679.168000
ints: 106142.824000
floats: 144527.936000
sum: inf
sum2: 67031739228347449344.000000
mild optimization
begin: 0.014000
floats: 33365.716000
ints: 33365.752000
floats: 49180.112000
ints: 50145.824000
floats: 80342.648000
sum: inf
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.014000
floats: 31515.560000
ints: 31515.604000
floats: 47947.088000
ints: 49016.240000
floats: 78929.784000
sum: inf
sum2: 67031739228347441152.000000

Это более старый ПК, около 2004 года, в противном случае он слегка загружен.

Похоже, это замедлило ситуацию.Меньше нуля, чтобы сделать арифметику с, возможно?Вот как выглядит много случайных битовых комбинаций.Или такие значения, как 0,0000000000000000000000000382652.Как только это добавляется, скажем, к 0,1, младшие биты, как правило, удаляются.

1 голос
/ 03 сентября 2010

Все случайные 64-битные целые числа имеют нули в старших 32 битах, поскольку rand() возвращает 32-битные значения (по крайней мере, для gcc на 32-битной платформе). Таким образом, все двойные числа будут денормализованы, так как переотолкованные битовые комбинации целых чисел будут иметь ноль для поля экспоненты. Добавление 0,1 к денормализованному значению дает нормализованное значение (очень близкое к 0,1).

Таким образом, каждая строка ffun2 является умножением на денормализованное значение; каждая строка ffun3 является умножением на нормализованное значение. Глядя на сгенерированную сборку, я вижу, что множители вычисляются до цикла; в каждом случае цикл состоит только из умножений. Наиболее вероятное объяснение разницы во времени выполнения состоит в том, что умножение занимает гораздо больше времени, если множитель денормализован.

Что касается последнего вопроса: арифметика с плавающей запятой (в частности, двойная точность) намного сложнее, чем целочисленная арифметика, поэтому на достаточно современном конвейерном процессоре каждая инструкция будет выполняться дольше.

1 голос
/ 03 сентября 2010

Вы не сбрасываете begin между тестами, поэтому ваши временные числа сложно интерпретировать.Может быть, это источник вашего замешательства?

...