Оптимизируются ли тривиальные операции?
умножение на 1 (да и на другие степени 2-ки).Если a*b - то как ты себе это представляешь? Для того, чтобы понять, надо ли что-то оптимизировать - надо понять, не является ли b степенью двойки, а на это у современных процессоров навряд ли уйдёт меньше тактов, чем на собственно умножение без всякой оптимизации.
Если a*C (C - константа) - то запросто. Например, компилятор, если видит, что C - степень двойки, тупо заменяет это выражение на a<<D.
В компиляторе при возможности арифметику упрощают. В процах не силён, но знаю, что xor eax, eax точно является "особым случаем" .
В первую очередь интересует умножение на 0.
Даже так - известен наиболее вероятный кандидат на 0 (из двух операндов) - то есть даже если второй операнд = 0 - это переживаемо.
Или даже в такой трактовке оптимизация отсутствует?
Если a*b - то как ты себе это представляешь?На самом деле встроить проверку равенства 0 одного из операндов умножения на уровне проца проблемы никакой (да и ресурсов тоже мало надо).
Вопрос скорее в том, есть ли процы, где это реализовано? Или этого нет?
Тогда почему нет: никому не надо, не приходило в голову, не окупается и тп?
Если a*C (C - константа) - то запросто. Например, компилятор,Это слишком тривиальный случай.
В компиляторе при возможности арифметику упрощаютЕсли уж зашла речь про оптимизаторы компиляторов, спрошу: если заданы 2 выражения, у которых есть общие операции, компиляторы это оптимизируют?
Грубо:
x=a+b*c
y=b*c+d
чтобы умножение выполнилось только 1 раз.
что xor eax, eax точно является "особым случаем"Ну тут и проверять, и выполнять нечего - результат всегда один и тот же
Вопрос скорее в том, есть ли процы, где это реализовано? Или этого нет?А зачем? С точки зрения языка всегда есть последовательность вычисления выражений, так что умножение f*0 или 0*f всегда будет вычислять результат f.
Он имеет в виду ситуацию, когда оба множителя - переменные, а не результаты вызова функций
Он имеет в виду ситуацию, когда оба множителя - переменные, а не результаты вызова функцийДумаю, что на большинстве процессоров умножение занимает 1 такт. Так что смысла в проверках нет.
А с "или" и "и" такая оптимизация, во-первых - для простоты разработки (чтобы можно было напистаь что-нибудь вроде fopen(...) or die('shit happens' а во-вторых - чтобы оптимизировать не какие-то там логические операции, а возможные вызовы тяжёлых функций.
А с "или" и "и" такая оптимизация, во-первых - для простоты разработки (чтобы можно было напистаь что-нибудь вроде fopen(...) or die('shit happens' а во-вторых - чтобы оптимизировать не какие-то там логические операции, а возможные вызовы тяжёлых функций.В принципе ты повторил мои слова выше. В языках программирования boolean выражения часто вычисляются до результата: второй or не вычисляется, если первый true; тоже касается and. Здесь же речь идёт об умножении.
Если уж зашла речь про оптимизаторы компиляторов, спрошу: если заданы 2 выражения, у которых есть общие операции, компиляторы это оптимизируют?Да, стараются, по крайней мере
Думаю, что на большинстве процессоров умножение занимает 1 такт.С каких это пор? GPU-хи не в счёт.
function test {
int i = 0;
int res = 1;
for(i=0;i++;i<10000) {
res = (res * i)%4958383;
}
int x = 0;
for(i=0;i++;i<1000000) {
x += res ^= x;
}
print 'OK';
}
, превращая её в
function test {
print 'OK';
}
The instruction sets generally include a full set of vector instructions, including multiply, invert and trace
Multiplication
Only unpacked representation is supported. Only two single digit numbers can be multiplied.
First the digits are multiplied as usual using mul.
Then the result is adjusted using aam (ASCII adjust for multiplication).
The processor divides the result by ten, storing the quotient (just the integral part) in the most significant byte of the result and the remainder in the least significant byte of the result.
One advantage of 3DNow! is that it is possible to add or multiply the two numbers that are stored in the same register. With SSE, each number can only be combined with a number in the same position in another register. This capability, known as horizontal in Intel terminology, was the major addition to the SSE3 instruction set.
PMULHRSW Packed Multiply High with Round and Scale treat the sixteen-bit words in registers A and B as signed 15-bit fixed-point numbers between -1 and 1 (eg 0x4000 is treated as 0.5 and 0xa000 as -0.75 and multiply them together with correct rounding.
PMADDUBSW Multiply and Add Packed Signed and Unsigned Bytes Take the bytes in registers A and B, multiply them together, add pairs, signed-saturate and store. IE [a0 a1 a2 ...] pmaddubsw [b0 b1 b2 ...] = [satsw(a0b0+a1b1) satsw(a2b2+a3b3) ...]
ЗЫ: А вообще, всё это хуйня. Потому что:
PMULLD, PMULDQ Packed signed/unsigned 16->32-bit and 32->64-bit integer multiply
MUL Unsigned multiply
Оптимизируются ли тривиальные операции?Да.
Например:
real x = y==0 ? 0. : y*z
Где тут хоть слово про "умножение занимает один такт"?
на обычных современных процессорах ускорение такой оптимизацией можно получить только если у тебя там в 99.9% случаев 0.
но в этом случае стоит переделать программу более основательно.
На самом деле задача такая:
есть индексированное множество чисел и итерационное выражение, которое на границе этого множества будет зависеть от чисел, которых нет (то есть они за пределами индексов множества и считаются равными 0).
Соответственно можно подходить противоположно:
1. Везде при вычислении таких выражений втыкать проверки на выход индексов за пределы
2. Добавить к множеству границу, которая всегда хранит 0 и просто в ситуации выхода индексов за пределы будет вычисляться выражение от нулевых значений (правда если значение может зависеть не только от соседних элементов, но и от +-10-го, например, то объем доп памяти сильно возрастет )
Можно конечно разбить все это множество индексов на области и для каждой проводить вычисления, но это приведет к многократному дублированию кода (или компилятор как раз так и оптимизирует код, развернув такие условия?).
Или это как-то можно сделать красиво?
Не, 0 там будет в среднем меньше чем в 10% случаев.минус 100 за тред
На самом деле задача такая:
есть индексированное множество чисел и итерационное выражение, которое на границе этого множества будет зависеть от чисел, которых нет (то есть они за пределами индексов множества и считаются равными 0).
Соответственно можно подходить противоположно:
1. Везде при вычислении таких выражений втыкать проверки на выход индексов за пределы
2. Добавить к множеству границу, которая всегда хранит 0 и просто в ситуации выхода индексов за пределы будет вычисляться выражение от нулевых значений (правда если значение может зависеть не только от соседних элементов, но и от +-10-го, например, то объем доп памяти сильно возрастет )
Можно конечно разбить все это множество индексов на области и для каждой проводить вычисления, но это приведет к многократному дублированию кода (или компилятор как раз так и оптимизирует код, развернув такие условия?).
Или это как-то можно сделать красиво?
А ещё, например, в Haskell, есть такая штука, как ленивые вычисления.
Напиши геттер, который возвращает 0 за пределами и всё...
А уже, когда всё заработает, оптимизируй. А так у тебя с большой вероятностью получится быстрый говнокод,
который нигде боле не заюзаешь.
Может, оптимизиция и не нужна буит.
минус 100 за тред
Можешь ставить хоть минус лимон, но пока ты не аргументируешь свою позицию - грош цена твоей оценке
Но, по крайней мере, процессор знает о том, что такое "умножение" (т.е. компилятор не переделывает это в сложение а, значит, вполне может и сам провести необходимую оптимизацию.
а, значит, вполне может и сам провести необходимую оптимизациюто что он может и так понятно
вопрос изаначальный и был: есть ли это гденить?
Спроси у интела
Прежде, чем интересоваться оптимизацией на нижних уровнях (компилятора, процессора) сначала
1) подумай, так ли нужна на самом деле оптимизация (для этого надо как минимум иметь рабочую версию программы, скорость работы которой не устраивает) — принцип KISS еще никто не отменял.
2) оптимизируй в первую очередь вычислительный алгоритм.
Судя по твоим постам ты не сделал ни первого, ни второго.
Твоя задача никак не связана с твоим вопросом. Оба решения плохи.Спорно.
Прежде, чем интересоваться оптимизацией на нижних уровнях (компилятора, процессора) сначала
1) подумай, так ли нужна на самом деле оптимизация (для этого надо как минимум иметь рабочую версию программы, скорость работы которой не устраивает) — принцип KISS еще никто не отменял.
2) оптимизируй в первую очередь вычислительный алгоритм.
Это все понятно.
Судя по твоим постам ты не сделал ни первого, ни второго.
Вот чтобы сделать это лучше, я и решил просветить для себя этот вопрос
В твоей задаче правильней добавить ещё строчку и столбец с нулями.
Но вообще тебе бы лучше убить себя или по крайней мере не заниматься оптимизацией, раз уж не умеешь.
ни надо никого убивать. автор молодец что задумался и поинтересовался. ну не умеет он (не имел источника знаний/опыта/желания оптимизировать а тут просто заинтересовался, какой вариант лучше будет работать. ну и что?
В твоей задаче правильней добавить ещё строчку и столбец с нулями.а вот совсем не факт. смотри, он же писал:
1. Везде при вычислении таких выражений втыкать проверки на выход индексов за пределы
2. Добавить к множеству границу, которая всегда хранит 0 и просто в ситуации выхода индексов за пределы будет вычисляться выражение от нулевых значений (правда если значение может зависеть не только от соседних элементов, но и от +-10-го, например, то объем доп памяти сильно возрастет )
оценим объем вычислений
1. несколько сравнений (если у нас прямоугольная матрица, то это 4 сравнения)
2. умножение (для получения адреса элемента. для прямоугольной опять же тут и сложение добавляется) и вычисление выражения от нулевых значений
Умножение всегда работает быстрее, чем условный переход. В современных процах условный переход может работать вообще невозможно, чудовищно медленней, потому что выносит моск параллелизатору.Спасибо, это хороший аргумент, я как-то упустил его из виду.
В твоей задаче правильней добавить ещё строчку и столбец с нулями.Это вариант №2 из тех, что я предлагал
Но вообще тебе бы лучше убить себя или по крайней мере не заниматься оптимизацией, раз уж не умеешь.Ты чувствуется еще у утробе матери был супермегаоптимизатором
оценим объем вычисленийПримерно так, но не совсем - все эти операции носят массовый характер (обход и перевычисление всего множества).
1. несколько сравнений (если у нас прямоугольная матрица, то это 4 сравнения)
2. умножение (для получения адреса элемента. для прямоугольной опять же тут и сложение добавляется) и вычисление выражения от нулевых значений
Поэтому оценка для 1 такая (для 2д а вот оценка для 2 не совсем такая, так как элементы можно перебирать последовательно, завести кучу (ну или сколько там надо) итераторов и умножения для получения адреса заменить инкрементом (+1, +длина строки, +размер плоскости).
в чем у тебя функция заключается? наверное она не будет дешевле умножения или же если она составная, то опять тратим одно сравнение на проверку значения аргумента, типа если ноль, то ф тоже 0
В принципе такие вещи могли бы оптимизироваться на уровне проца.Интересно, как ты себе это представляешь? Инструкция ушла, канал был занят, тот же такт на запуск конвейера...
Может, суперскаляры и проводят такие сравнения, хотя это, ИМХО, лишняя AL, которую полезно было бы употребить на что-нибудь иное.
В списке стандартных оптимизаций есть нестинг, когда основной цикл идет без учета редких случаев, а в случае редкого случая ( ) происходит прыжок на компенсирующий код. Однако проверка равенста нулю множителя - это, ИМХО, маразм; в нынешних процах она вполне может занять времени и устройств больше, чем само по себе умножение.
основной баг оратора посоветовшего добавить в итоге рамочку из нулей я заметил в том, что он не учел время на вычисление значения функции в нуле.Функция как раз в этом смысле простая: сумма произведений пар элементов множества.
в чем у тебя функция заключается?
То есть если одно из чисел = 0, то "пропадает" только 1 умножение.
Интересно, как ты себе это представляешь?В блоке умножения добавляется две схемы, каждая реализующая логическое сложение всех битов соответствующего операнда с инверсией выхода (чтобы 1 означала что операнд = 0).
Они также соединяются логическим сложением.
То есть сразу после помещения операндов в регистры мы знаем, имеет ли смысл умножать или нет (если хотя бы один из операндов 0).
Сильно сомневаюсь, что такая схема будет работать столько же, сколько и само умножение (но если для современных процов это так - именно это я и хотел прояснить).
В транзисторах это должно быть немного - несколько сотен на блок умножения (правда в нюансах современной схемотехники не силен, но все равно в масштабах блока умножения это должны быть копейки).
Никаких лишних переходов из-за этого не получается.
на уровне проца все это не имеет смысла, т.к. что умножение на нормальное число, что хитрое умножение на 0 - и то, и другое занимает один такт.
все это имеет смысл только на логическом уровне - когда мы видим, что целый большой кусок умножается на 0: 0 * f(...).Ну и тогда надо, чтобы это была не прозрачная оптимизация, а очень даже явная, чтобы о ней кричали на каждом углу.
Иначе кто-нибудь может не понять, почему же в выражении x*f(...) его функция f иногда не вызывается.
Иначе кто-нибудь может не понять, почему же в выражении x*f(...) его функция f иногда не вызываетсяэто важно только для функций, которые имеют побочные эффекты, в тех же ФЯ - компилятор знает, какие функции имеют такую побочку, а какие нет.
на уровне проца ... умножение на нормальное число ... занимает один такт.Уже второй раз в этой теме. Хоть один пример такого турбореактивного процессора?
FMUL Floating point multiplyили
FMULP Floating point multiply and pop
variations/
operand 8087 287 387 486 Pentium
fmul reg s 90-105 90-105 29-52 16 3/1 FX
fmul reg 130-145 130-145 46-57 16 3/1 FX
fmul mem32 (110-125)+EA 110-125 27-35 11 3/1 FX
fmul mem64 (154-168)+EA 154-168 32-57 14 3/1 FX
fmulp reg s 94-108 94-108 29-52 16 3/1 FX
fmulp reg 134-148 134-148 29-57 16 3/1 FX
s = register with 40 trailing zeros in fraction
mulsd xmm, xmm
latency - 5, throughput - 1-2
А то учитывая, что в языках обычно 4 максимум 8-байтные типы данных (и те только на 64-х битных процах нормально(по скорости) работают) как-то в это не верится
Или надо использовать специальные библиотеки/компиляторы?
latency - 5Не 1 же!
А первый квот что доказывает?
да же на первом pentium - fmul занимает 1-3 такта
$ cat mul-latency.c
int main(int k)
{
int i;
double l = k, j = 1;
for (i=1300000000; i; i--)
j *= l;
return (int)j;
}
$ gcc -O2 mul-latency.c -funroll-loops -march=pentium-m
$ time ./a.out
real 0m5.127s
user 0m5.054s
sys 0m0.007s
$ gcc -O2 mul-latency.c -funroll-loops -march=pentium-m -mfpmath=sse
$ time ./a.out
real 0m5.307s
user 0m5.079s
sys 0m0.016s
затестил на AMD Athlon(tm) 64 Processor 3000+ (1.8GHz у него получилось не меньше 4 тактов на умножение с плавающей точкой.
Я не знаю, откуда взяты числа, приведённые в первом квоте, но тестирование на celeron m 1.3GHz показывает 5 тактов для вещественного умножения:Мож чего не понимаю... но где в твоем посте такты? Или ты из времени путем деления получил?
code:
$ cat mul-latency.c
int main(int k)
{
int i;
double l = k, j = 1;
for (i=1300000000; i; i--)
j *= l;
return (int)j;
}
$ gcc -O2 mul-latency.c -funroll-loops -march=pentium-m
$ time ./a.out
real 0m5.127s
user 0m5.054s
sys 0m0.007s
$ gcc -O2 mul-latency.c -funroll-loops -march=pentium-m -mfpmath=sse
$ time ./a.out
real 0m5.307s
user 0m5.079s
sys 0m0.016s
затестил на AMD Athlon(tm) 64 Processor 3000+ (1.8GHz у него получилось не меньше 4 тактов на умножение с плавающей точкой.
во-вторых, стоило разбавить умножения другими операциями, потому что сейчас ты тестируешь самой плохой вариант - когда помаксимуму загружен FPU, а остальные блоки простаивают - к реальной программе это имеет отдаленное отношение
Что, у тебя там в этом цикле больше ничего не происходит? Я вот вижу там навскидку проверку i!=0 и i-- - они, знаешь ли, не нулевое время делаются.
Или там всё как-то мегаоптимизировано?
Или надо использовать специальные библиотеки/компиляторы?Надеюсь, ты не думаешь, что gcc тебе выдаст оптимальнейший код для PIV?
Моноид, у тебя экзешник занимает порядка двух гигов, да?
Пиздец ваще, форум заполоняет абортивный материал, мне страшно!
не тупи, оно развернуло десяток итераций, этого достаточно чтоб loop уже не мешал.
Было бы интересно поглядеть на то, что оно действительно сделало
gcc -W -Wall -O2 -march=k8 -funroll-loops -S test.c
test.c:
int main(int k)
{
int i;
double l = k, j = 1;
for (i=1800000000; i; i--)
j *= l;
return (int)j;
}
test.s:
.file "test.c"
.text
.p2align 415
.globl main
.type main, @function
main:
.LFB2:
cvtsi2sd %edi, %xmm1
movl $1799999999, %eax
subl $7, %eax
movsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
.p2align 47
.L2:
mulsd %xmm1, %xmm0
subl $8, %eax
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
jne .L2
cvttsd2si %xmm0, %eax
ret
.LFE2:
.size main, .-main
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB2
.long .LFE2-.LFB2
.uleb128 0x0
.align 8
.LEFDE1:
.ident "GCC: (GNU) 4.2.2 (Gentoo 4.2.2 p1.0)"
.section .note.GNU-stack,"",@progbits
dsme /tmp $ time ./test.exe; time ./test.exe; time ./test.exe; time ./test.exe;
real 0m4.157s
user 0m4.160s
sys 0m0.000s
real 0m4.158s
user 0m4.110s
sys 0m0.010s
real 0m4.135s
user 0m4.130s
sys 0m0.000s
real 0m4.140s
user 0m4.130s
sys 0m0.000s
/proc/cpuinfo
model name : AMD Athlon(tm) 64 Processor 3000+
cpu MHz : 1800.000
стоило разбавить умножения другими операциями, потому что сейчас ты тестируешь самой плохой вариант - когда помаксимуму загружен FPU, а остальные блоки простаиваютИнтересно, почему на это никто не отвечает? Что, когда остальные блоки чем-то заняты, FPU магически начинает быстрее умножать?
И что за индивид интеллектуального уровня пенартура воспользовался аккаунтом ФиДжея?
Если что, в моём случае в цикле было 10 инструкций: 8 умножений, декремент регистра (с установкой флагов условный переход.
Может кто-то может предложить другую методику измерения латентности умножения? Может она даже покажет результат в 1 такт? (собственно, я начал писать в этой теме только потому, что уверенно утверждался такой вот факт, который я считаю неверным)
Насчёт умножения за один такт: прелюбопытнейший факт, мне не удалось заставить cl развернуть цикл, я развернул его ручками (тоже в 8 инструкций) и понял, почему cl его не разворачивал. Время не изменилось вообще. То есть за время выполнения fmul процессор умудряется сожрать декремент eax и условный джамп параллельно. Я был удивлён.
И такой вот код
int main(int k)
{
int i, zzz = 13;
double l = k, j = 1;
#define op do {j *= l; zzz *= k; } while(0)
for (i=200000000/8; i; i--)
{
op;
op;
op;
op;
op;
op;
op;
op;
}
return (int)j + zzz;
}
превращается в
$main:
; Line 16
imul esi, eax
fmul ST(0 ST(1)
fmul ST(0 ST(1)
fmul ST(0 ST(1)
imul esi, eax
fmul ST(0 ST(1)
fmul ST(0 ST(1)
fmul ST(0 ST(1)
imul esi, eax
fmul ST(0 ST(1)
fmul ST(0 ST(1)
imul esi, eax
imul esi, eax
imul esi, eax
imul esi, eax
imul esi, eax
sub ecx, 1
jne SHORT $main
и тоже жрёт те же ~4.3 такта на итерацию (неразвёрнутую).
imul жрёт три такта. И тоже полностью параллелится с остальной частью цикла.
Более того, то ли с кэшем инструкций какая-то фигня, то ли вижуальник не понимает особенностей моего процессора, но неразвёрнутый цикл с imul+fmul получается немножко быстрее развёрнутого.
Включение SSE2 чуть-чуть ускоряет, вместо fmul юзается mulsd, который, видимо, ещё лучше параллелится. И, кстати, невзирая на то, что код выглядит так:
$main:
; Line 24
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
imul ecx, eax
mulsd xmm1, xmm0
imul ecx, eax
mulsd xmm1, xmm0
imul ecx, eax
mulsd xmm1, xmm0
imul ecx, eax
sub edx, 1
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
mulsd xmm1, xmm0
jne SHORT $main
... всё равно четыре такта на итерацию. Чота либо я не понимаю чего-то, либо унутре athlon64 3000+ стоит какая-то дико умная неонка.
Так вот, по поводу одного такта на умножение: да, не один =). Но на практике можно считать, что один, потому что вся окружающая хрень в требующиеся четыре такта запихнётся. Может даже вообще бесплатно получится как бы! Ну, как у меня получается — казалось бы, умножение даблов жрёт четыре такта, но можно к нему добавить ещё целочисленное умножение (3 такта декремент(типа 1 такт) и джамп (хз, 1-2 и всё равно получится четыре такта, а не 9-10, как можно было бы ожидать.
Интересно, почему на это никто не отвечает? Что, когда остальные блоки чем-то заняты, FPU магически начинает быстрее умножать?вот FJ тебе развернуто правильно ответил
сегодняшний процессор - это многопараллельный модуль, поэтому чистые оценки выполнения мало интересуют при написании реальных программ, больше интересует некая приблеженная оценка - насколько программа станет медленнее, если в нее добавить ту, или иную инструкцию.
соответственно для умножения - можно считать, что эта приблеженная оценка - 1 такт.
А насчет умножения на 0 - если оно не целочисленное, то результат может и не нулем быть.
А разве целые нонче не в даблах хранятся?
А насчет умножения на 0 - если оно не целочисленное, то результат может и не нулем быть.Это как это?
Если так происходит - это ошибка
Или процы уже дошли до того, что нулем как таковым не оперируют, а оперируют минимальным числом?
$ cat nan.c
#include <stdio.h>
int main(void)
{
double inf_p = 1.0/0.0, inf_n = (-1.0)/0.0, nan = 0.0/0.0;
printf ("+INF: %g, +INF*0: %g, -INF: %g, -INF*0: %g, NaN: %g, NaN*0: %g\n",
inf_p, inf_p*0.0, inf_n, inf_n*0.0, nan, nan*0.0);
return 0;
}
$ gcc nan.c
$ ./a.out
+INF: inf, +INF*0: nan, -INF: -inf, -INF*0: nan, NaN: nan, NaN*0: nan
Кстати, любопытный факт: нули в IEEE 754 тоже разных знаков, и нейтральным элементом относительно сложения является именно отрицательный ноль:
(+0) + (+0) = (+0)
(-0) + (-0) = (-0)
(-0) + (+0) = (+0)
, , : я знаю про такое явление как параллелизм на уровне команд, спасибо. Вы что, ребята, вообще? Я не хотел превращать тред в разгорячённый спор со взаимными оскорблениями. Извините. Но молча пропустить неправильное утверждение тоже не смог. Ещё раз. Есть конкретное понятие "латентности операции". Для большинства арифметических операций она постоянна. Для загрузки числа из памяти, например, латентность переменна, так как оно может лежать в кеше или в оперативке. Утверждение про "умножение занимает один такт" в контексте параллелизма на уровне команд некорректно, хотя бы потому, что никак не ссылается на то, лежит ли это умножение на критическом пути вычислений, или нет. Вот если я скажу, что поскольку декодеры команд популярных процессоров пропускают не более трёх инструкций за такт, поэтому при достаточном параллелизме на уровне команд можно считать, что умножение занимает треть такта, как вы к этому отнесётесь?
Вот если я скажу, что поскольку декодеры команд популярных процессоров пропускают не более трёх инструкций за такт, поэтому при достаточном параллелизме на уровне команд можно считать, что умножение занимает треть такта, как вы к этому отнесётесь?Ну это, собственно, и есть затраты на умножение в момент пиковой производительности.
поэтому при достаточном параллелизме на уровне команд можно считать, что умножение занимает треть такта, как вы к этому отнесётесь?отлично.
если это справедливо для реальной программы, то чем плоха оценка - что умножение выполняется за треть такта?
Лингвистически такая конструкция выглядит странно. Что физически могло выполниться за треть такта? oO
Ты не поверишь - операция с вещественными аргументами!
Но я всегда думал, что такт задает наименьший промежуток между стационарными состояниями на процессоре. И только в этих состояниях ясно, что операция начала выполняться или выполнилась. Это не так?
---
А хз. Тактовая частота == Clock frequency, то есть это частота центрального кварца на процессоре, не больше и не меньше. Разумеется, внутри процессора есть тыща умножителей и делителей частоты, которые синхронизируют разнообразные процессы, так что хотя некая корреляция между тактовой частотой и реальной латентностью операций есть, статуса закона у неё нет.
Моноид, конечно, прав, говоря, что понятие латентности (то есть минимального времени выполнения данной операции in respect to clock frequency на данном конкретном процессоре) вполне определено, наиболее соответствует неформальному определению "длительность операции" и у операции умножения на исследованных нами современных процах латентность вовсе не 1 такт. И, кстати, видимо меньше, чем у условного перехода. Окей, я был терминологически неправ!
Что касается усреднённого времени, я теперь даже и не знаю. Я проверил — в том же коде дописывание if (k != 0) (я смотрел в листинг, ничего не отоптимизировалось) тоже параллелится с самим умножением (укладываясь в те же три такта, независимо от того, чему равно k). Пытаться построить сложный случай, чтобы выяснить, как джамп влияет на кэш инструкций и предсказатель переходов, мне влом.
для разумной оценки нужна более сложная модель чем время_выполнения(кода_кусок) = сумма(среднее_время(инструкция
видел статейку где авторы ужасались от хаотичного поведения современных процессоров -— из-за сложной работы с памятью и кэшами время исполнения кода крайне нестабильно.
Оставить комментарий
durka82
Например логические выражения не вычисляются дальше, если результат уже очевиден.А есть ли такое для обычной арифметики?
Например умножение на 0, сложение с 0 и умножение на 1 (да и на другие степени 2-ки).
В принципе такие вещи могли бы оптимизироваться на уровне проца.
Или это все из области фантастики?
Вот например в архитектуре АРМ есть похожие вещи (условное выполнение).
Правда в этом случае они не подходят (правда я не спец по этой архитектуре - мб там есть и такие возможности).
Если таких оптимизаций нет в проце, то мб их делают на уровне компиляторов?