Здравствуйте, фонарь, Вы писали:
Ф>что можно сказать по поводу такого алгоритма
Ф>Ф>a -= b;
Ф>b += a;
Ф>a = b - a;
Ф>
Ф>против обычной замены?
Поскольку у Intel x86 нет операций память-память, за исключением строковых (movsb и т.п.), то по-любому возникнет потребность в промежуточном регистре.
extern void swap1(unsigned int& a, unsigned int& b)
{
unsigned int x = a;
a = b;
b = x;
// реконструкция ассемблерного кода (*1*)
unsigned int x = a; // *2*
unsigned int y = b;
a = y;
b = x;
}
extern void swap2(unsigned int& a, unsigned int& b)
{
a -= b;
b += a;
a -= b;
// реконструкция
unsigned int x = a;
unsigned int y = b;
x -= y;
a = x; // *3*
y += x;
b = y;
x -= y;
a = x;
}
extern unsigned int p = 100, q = 200;
int main()
{
swap1(p,q);
swap2(p,q);
return 0;
}
*1* — получить ассемблерный код можно, запустив cl -Ox -Fa test.cpp
(-Ox — оптимизировать все).
Реконструировал вручную. Все-таки С выразительнее ассемблера
*2* — x,y — регистры. В ассемблерном листинге видно, что требуются 4 регистра: адрес a, адрес b (переданные параметры), x, y. Поскольку конвенция предполагает несохранение только трех — eax,ecx,edx, то четвертый — esi — сохраняется в стеке (push esi / pop esi).
*3* — не знаю, зачем пишется промежуточное значение a. Без этой строки вполне можно обойтись. Тем не менее...
Итого
* swap1 имеет 2 чтения и 2 записи в память.
* swap2 — 2 чтения, 3(2) записи и
3 арифметических операции регистр-регистр.
* Если бы swap2 использовал арифметику память-регистр, то ему пришлось бы неоднократно читать значения из памяти (что там получилось?). Естественно, это было бы еще медленнее.