Проведение теста: сортировка массива (array[1..20] of integer) методом пузырька. Всего было проведено 30 тестов : 10 c n= 10^4; 10 c n= 10^5; 10 c n= 10^6; и один тест с n=10^7 (один, т.к. с увеличением степени n на 1, время проведения теста увеличивается соответственно в 10 раз)
Результат: Во всех случаях (каждый тест - новый массив) самой быстрой конструкцией оказалась "While ... do...", следом за ней "If ... then Goto..."
mj
13.03.2004 22:09
на процессорах P2 и ниже FOR быстрее чем WHILEдолжно работать на новых процах наоборот, это связано с внутренней конструкцией процессора.
Atos
16.03.2004 18:44
И Да ЗдравствуетИтерация! Пример: старая добрая задачка о Ханойской башне (нерекурсивный вариант).
trminator
15.05.2004 22:53
Ускорение вычислений mod
Програма считает 50 - 55 секунд (!), до 8 000 000 экспериментов. Вычисления типа:
c0 := (a * c0 + B) mod m
где m = 2^24 (генератор случайных чисел, если кому интересно)
Код быстрее:
c0 := (a * c0 + B) and (m - 1)
Ускорение -- более чем в 50 раз. Вот так... Прога работает около секунды - полутора. Когда берем остаток от деления (на степень двойки!), фактически, мы просто оставляем последние m-1 бит числа. А это можно сделать и and'ом
P@sh@
16.05.2004 0:08
Деление на степень двойки
Аналогично быстрое деление на степень двойки - shr, а умножение - shl
если мне не изменяет память, на команду DIV (деление) 486-е процы тратили до 70-ти тактов! (для сравнения - битовые команды типа AND/OR и сдвиги выполняются за 1-2 такта)
P@sh@
17.05.2004 22:42
Разворачивание циклов. -=loops unrolling=-
Один из методов оптимизации больших циклов - loops unrolling - "разворачивание"... тело цикла можно скопировать два/четыре раза, уменьшив во столько же раз кол-во итераций... глядишь, процессору удасться их распараллелить, да и на jump-ы время меньше тратится...
trminator
19.05.2004 23:35
P@sh@, сомневаюсь, что игра стОит свеч. Хотя попробовать можно... на jump'ы тратится не только время на их выполнение, если еще учесть, что может неверно сработать "предсказание" процессором, какую команду пускать на конвейер. Хотя это уже оптимизация будет не "качественная", а какая-то "количественная"...
ЗЫ: я ее все равно уже сдал...
Altair
25.05.2004 18:28
Процедуры FAR
Все процедуры в модулях, пишутся в расчете на дальнюю модель памяти. При этом тратится 1 байт и несколько милисекунд на запуск такой процедуры.
BlackShadow
25.05.2004 20:26
Специально для Oleg_Z: В BP есть такая возможность при компиляции модуля указать, что тот может быть оверлейным ({$O+}). Тогда в проге можно будет использовать его таким образом: [PROGRAM][OVERLAY BUFFER] [PROGRAM][UNIT1][Y BUFFER] [PROGRAM][U2][RLAY BUFFER] Т. е. модули остаются неприкомпиленными, а извращёнными (компилятся в .ovr, вроде) и их надо тягать с собой (как .bgi), а когда надо вызвать чего-нибудь, то прога подгружает нужный модуль в память и работает с ним. Управлять буфером можно функциями из модуля Overlay. Короче отстой полный.
Цитата
Ну это не разработки борланда, это и в асме есть.
Покажешь где?
BlackShadow
26.05.2004 2:46
Описалова в книгах достаточно. Посмотрю чего - завтра отпишу. На самом деле, если в разных модулях находятся 2 частовызываемые функции, то на загрузке/выгрузке потеря будет... А кто автор вот этой загадочной книги по Asm'у?
trminator
27.05.2004 22:30
Как нам говорили на лекции по ОС, оверлей - аналог страничной организации памяти, когда неиспользуемые страницы можно скинуть на диск (swap, pagefile) Так что не совсем dll
trminator
27.05.2004 22:37
А про использование AssignCRT вместо Assign для записи в текстовый файл уже говорили? по Фаронову, наного быстрее
BlackShadow
28.05.2004 1:40
Ну и зкономии ради можно (олимпиадныю трюк) воспользоваться Assing(Input,'In.Txt'); Reset(Input); Assign(Output,'Out.Txt'); ReWrite(Output);
Altair
28.05.2004 1:44
Просто есть файловая переменная Input и output только это не ускорит и не уменьшит размера.
trminator
28.05.2004 3:04
Про сортировку. Понятно, что пузырек и компания никуда не годятся хотя если нужно отсортировать десяток элементов, самое оно
Есть распространенное мнение, что лучшая сортировка -- quicksort. Да, лучшая. В среднем (O(n*log(n))). В худшем случае ее время -- O(n*n). Если вам нужна гарантия, что ваша сортировка ВСЕГДА выполняется за нлогн, ИМХО, лучше выкинуть быструю сортировку.
Кстати, помогите идентифицировать сортировку. Кажется, это -- пирамидальная:
Код
procedure swap (i, j : word); var t : integer; begin t := a[i]; a[i] := a[j]; a[j] := t; end;
procedure sort (n, t : word); begin while ((t shl 1+1 <= n) and (a[t shl 1+1] > a[t]) or (t shl 1 <= n) and (a[t shl 1] > a[t])) do if (a[t shl 1+1] >= a[t shl 1]) and (t shl 1 +1 <= n) then begin swap (t shl 1 +1, t); t := t shl 1+1; end else begin swap (t shl 1, t); t := t shl 1; end; end;
Раскопал ее в развалинах своего винта. Она работает
BlackShadow
28.05.2004 15:36
trminator, да похоже на пирамидку. Oleg_Z, использование Input и OutPut действительно ничего не ускорят, но вот размер на объявлении двух новых ФАЙЛОВЫХ переменных сэкономит. Oleg_Z, извиняй, что так завис с Overlay'ем - малой мой книгу мне только сегодня выделил, а сутра мне не до этого было. Пока чай пил я пролистал главу про эту фигню и нашёл кое-что интересное. Во-первых .Ovr могут прилинковываться к EXE-шнику, но загружаться только по требованию,а во-вторых стандартный модуль Overlay умеет работать с EMS! Т. е. при загрузке программы все модули можно закачать в EMS, а оттуда по требованию скидывать в обычную память и работать с ними. Кратко о использовании. Оверлейным может быть только модуль откомпилированный с {$O+}, а так же ВСЕ(!) экспортируемые функции должны быть FAR. Т. е. проще дописать и {$F+}. Из стандартных оверлейными, вроде, могут быть только DOS и Printer. Как подключить такой модуль? Вот так:
Код
Uses MyOvrU; {$O MyOvrU}
Но проблема в том, что НИКАКОЙ инициализации не может быть в таких модулях до инициализации менеджера оверлеев. Но, вобщем, и это не проблема. Давай вспомним, что происходит при запуске такой вот программы:
Код
Uses U1,U2,U3; Begin End.
Вызывается стандартный загрузчик Pascal'я, который дописывается в прогу как Entry Point, а затем поочерёдно запускаются блоки инициализации модулей U1,U2,U3 и именно в этом(!) порядке. Затем выполняется Begin-End, после чего управление переходит к Pascal'евском "завершителю". Так вот, если применить такой вот нехитрый трюк и создать модуль типа OvrInit, в котором будет инициализироваться менеджер оверлеев, а в программе записать его первым в списке используемых Unit'ов, то проблема инициализации снимается. Вот так. Это всё так, на словах, без единой функции модуля Overlay. Не буду их упоминать, т. к. не уверен, что вспомню в точности имена и параметры, но общий принцип таков. Сегодня вечером точно отпишу.
trminator
29.05.2004 19:32
Так... я не закончил про сортировки ИМХО, использовать, так пирамиду. Мои аргументы: * Обратите внимание на размер той процедуры, что я привел (я уже уверен, что это -- пирамида =). И сравните ее с размером quicksort'a. Что проще написАть по памяти? * Как я уже говорил, неплохо бы иметь гарантию, что ваша сортировка выполняется за n*log(n), а не за великий и ужастный n*n Особенно если вы участвуете в олимпиадах. И особенно если задачи составлял тов. Лопатин. Он любит quicksort
Ну и недостатки у пирамидки тоже есть как же без них. В среднем quicksort все же быстрее -- внутренний цикл у него самый быстрый.
BlackShadow
31.05.2004 15:08
trminator, по памяти, может, её и проще, но на рефлексах пишется и пузырёк и QuickSort
BlackShadow
31.05.2004 15:43
Oleg_Z, вот чего у меня в книге есть: Функции Overlay'а: OvrClearBuf - очищает буфер OvrGetBuf - возвр. размер буфера OvrGetRetry - возвр. размер области испытаний (что за она не въехал, но по-умолчанию тут 0). OvrInit - инициализирует систему Overla'ев и открывает оверлейный файл. OvrInitEMS - загружает оверлейный файл в память EMS, если это возможно. OvrSetBuf - устанавливает размер буфера OvrSetRetry - догадайся сам, хотя не стоит Так же тут описана переменная OvrResult:Integer, которая может быть OvrOK(0) - всё пучком OvrError(-1) - что-то не пучком OvrNotFound(-2) - файл не нашёл OvrNoMemory(-3) - досадно как... OvrIOError(-4) - файл есть, атолку нет ObrNoEMSDriver(-5) - и такое бывает OvrNoEMSMemory(-6) - а драйвер тогда зачем? Ещё в модуле есть возможность переопределить такую фигню:
Type OvrReadFunc=Function(OvrSeg:Word):Integeer; Var OvrReadBuf:OvrReadFunc; { *** } OvrFileMode:Byte;
Но зачем и что тут к чему я тоже что-то не того... А ещё в System есть стадо переменных предназначенных для Overlay'а: OvrCodeList:Word=0 - список сегментов кода OvrDebugPtr:Pointer=Nil - для отладчика OvrDOSHandle:Word=0 - для админа (скорее всего Handle файла) OvrEMSHandle:Word=0 - Handle EMS-куска OvrHeapEnd,OvrHeadOrg,OvrHeadPtr,OvrHeapSize:Word=0 - это если без EMS OvrLoadList:Word=0 - "используется администратором оверлеев". И без коментариев. Потешное замечание сделано в этой книге: не надо размещать в Overla'ях обработчики прерываний и данные для RegisterBGIDriver и RegisterBGIFont
Далее идёт пример:
{$M 16384,65536,655360} {$D-,F+} {D- для того, чтобы Deug-Data не скидывались в Exe-шник. Тогда можно .Ovr приаттачить к самому .Exe}
Uses Crt,Overlay,Ovr_Init,Ovr1,Ovr2;
Var ch:Char;
Procedure UseOverlay; Begin OutPut1 {Из Ovr1} End;
Begin Repeat clrScr; Write('Press any key to start ovrlays or ESC to quit'); ch:=ReadKey; WriteLn; If ch<>#27 Then UseOverlay Until ch=#27 End.
Unit OvrInit; Interface Implementation Uses Overlay,Crt; CONST OvrBufSize=100000; Begin ClrScr; OvrInit(ParamStr(0)); {!!! Предполагается, что .Ovr прилеплен к .Exe} If OvrResult<>OvrOk Then Begin WriteLn('ERROR!!!); Halt End; OvrInitEMS; If OvrResult<>OvrOk Then Begin WriteLn('ERROR!!!); Halt End; OvrSetBuf(OvrBufSize); If OvrResult<>OvrOk Then Begin WriteLn('ERROR!!!); Halt End; OvrSetRetry(OvrBufSize Div 3) {Div 3 - оптимальный вариант, но всё ещё не понятно, что за область такая} End.
Далее берём и Build'им саму прогу. А потом выходим из BP и пишем в cmd Copy /B Proga.Exe+Overlay.Ovr (или что-то типа того). Всё теперь можно запускать. Хотя рекомендуется не использовать Overlay во время Debug'а (какой геморрой надо преодолеть, чтобы запустить прогу). Вот так вот. Если ещё чего - спрашивай, может найду...
Altair
31.05.2004 19:44
Цитата
Oleg_Z, использование Input и OutPut действительно ничего не ускорят, но вот размер на объявлении двух новых ФАЙЛОВЫХ переменных сэкономит
То есть ты хочешь сказать, что если описать :
Код
var f:file; f1:file; f2:file ...
и если использовать только один, то под остальные будет память выделятся? Не может компилятор быть таким глупым.
------------- А как подсчитывать количество обращений, скажем к массиву, или к файлу, или к прерыванию? -------------
Кстати, пока не забыл - здесь говорили что-то о {$M}, так вот если в модуле, то компилер просто игнорирует!
BlackShadow
31.05.2004 20:04
Цитата
Не может компилятор быть таким глупым
А, если ты напишешь так:
Код
Var a,b:Integer; Begin a:=5; If a=1 Then b:=3 End.
Думаешь он под b ничего не выделит? А не веришь - проверь.
Код
Var a,b,c,d,e,f:File; q:Integer; Addra,Addrq:LongInt; Begin Addrq:=(Seg(q) Shl 4) + Ofs(q); Addra:=(Seg(a) Shl 4) + Ofs(a); WriteLn(SizeOf(a)); WriteLn(Addrq-Addra) End.
Сколько пишет?
Цитата
А как подсчитывать количество обращений, скажем к массиву, или к файлу, или к прерыванию?
1). Ты же за FPC взялся? Помнишь я тебе про свойства говорил? Вот так и подсчитать. 2). См п. 1 3). Перехватить и увеличивать счётчик.
Цитата
Кстати, пока не забыл - здесь говорили что-то о {$M}, так вот если в модуле, то компилер просто игнорирует!
{этой функции черт знает сколько лет. Ее я взял из программы PWLHack, старой прогрммы для взлома PWL, она хороша тем, что максимально оптимизированна.}
Код
Function BD.UpStr(S:String):String; {перевод строки в верхний регистр} Var I:Byte; Begin For I:=1 To ORD(S[0]) Do Begin Case S[I] Of 'a'..'z':S[I]:=Chr(Ord(S[I])-$20); 'а'..'п':S[I]:=Chr(Ord(S[I])-$20); 'р'..'я':S[I]:=Chr(Ord(S[I])-$50) End End; UpStr:=S End;
Мой вариант:
Код
Function BD.UpStr(S:String):String; {перевод строки в верхний регистр} Var I:Byte; Begin For I:=1 To ORD(S[0]) Do Case S[I] Of 'a'..'z', 'а'..'п': S[I]:=Dec(S[I], $20); 'р'..'я': S[I]:=Dec(S[I], $50) End; UpStr:=S End;
Altair
8.06.2004 17:52
Begin и end лишние видимо я поставил, а вот объединить два промежутка, это мысль. romtek, а скорость увеличится? ИМХО так только нагляднее, а сорость не увеличится.
BlackShadow
15.06.2004 16:32
Вспомнил один момент такой. При работе с дробями, практичнее фразы типа b:=a/10 заменять на b:=a*0.1 Ну, это так, мелочи...
Altair
15.06.2004 23:27
Цитата
Почему-то существует повальное мнение, что кроме применения ассемблера никак нельзя достигнуть увеличения скорости.
Нет, ассемблер нужно применять только для шлифовки критических участков кода (черт, это не моя фраза, и не помня откуда она ) Чаще сам алгоритм можно переработать используя только стандартные средства. Кстати конструкция if a and b then хуже, чем if a then if b then, т.к. в случае провала первой подцели (а), второя не будет проверятся!
--------- APAL, ИМХО пора бы эту тему закрепить.
Romtek
16.06.2004 2:08
Цитата(Oleg_Z)
черт, это не моя фраза, и не помня откуда она
моя вроде
Цитата(Oleg_Z)
т.к. в случае провала первой подцели (а), второя не будет проверятся!
Complete boolean evaluation (compiler option)
BlackShadow
16.06.2004 17:42
Да уж, Oleg_Z, это действительно отключается директивами. Другое дело - преобразование сложных логических вычислений. Например: (a And Not B) Or (Not a And B) = a Xor b.
Altair
16.06.2004 19:20
BlackShadow, в следующий раз оформляй логические выражения в тег кода. Насчет преобразования сложных логических вычислений - а все ли логические функции выполняются с одинаковай скоростью ??? Или есть смысл упрощать сложные логические конструкции? Например если 10^n раз проверяется
Код
Not(A and B)
То может это заменить на
Код
(Not a) or (not b)
Тогда мы избавляемся от and. Только есть ли в этом смысл? Думаю, что нет, а если есть, то при очень большом количестве повторов.
Romtek
16.06.2004 20:15
В первом случае имеем 2 операции, а во втором - 3
Altair
17.06.2004 0:24
Да, но во первых операция NOT ИМХО должна выполняться быстрее, а во вторых есть обратный ход.
Altair
17.06.2004 17:34
Вот еще: Если в программе используется write и writeln, то следует убрать последнюю, а использовать только write и управляющие символы. Доказательство преимущества этого способа: 1 байт с проги снимется, т.к. writeln и write процедуры вне кода описанны, и компилтруются в расчете на FAR. А так, вместо 2 байт на дальнюю модель памяти (т.е. на адрес процедуры) уйдет только 1 байт!
====== добавил позже =======
Облом. Я проверил все. 1. Логические операции бесполезно упрощать! 2. Writeln работает быстрее, чем write('ddfd',#13,#10), и к тому же на 100 кб(!!) меньше код получается (использовался один раз). 3. Сильно уменьшает код (на 150-200 кб) следующие директивы (применять только на уже оптимизированных программах)
Цитата
N+ - сопроцессор E+ - эмуляция сопроцессора I- - отключение проверок ввода/вывода R- - отключение проверок на границы массивов S- - отмена проверки на переполнение стека Q- - отмена проверок на границы типов (overflow, underflow) B- - быстрое вычисление логических условий D- - отключить информацию для отладки (пропадает возможность отлаживать программу!!!)
(взял с сурсов.) Очень полезные директивы. Только две первые не влияют на размер.
Altair
18.06.2004 18:05
REAL почти в 2 раза медленнее ВСЕХ других числовых типов
доказательство в присоединенном коде.
P@sh@
26.06.2004 11:41
Oleg_Z Real потому медленнее, что у него размер нестандартный - 48 бит, кто его такой выдумал, интересно ? а все более-менее современные процы оптимизированы на работу с 32/64-бит данными. к тому же этот тип не поддерживается сопроцессором, поэтому его либо надо переконвертать в нормальный, либо эмулировать...
насчет директив - все они есть в диалоговом окошке "Опции компилятора" или как его там. если убрать все проверки, тогда конечно меньше будет объем кода, да и быстрее чуть-чуть, а почему первые две не влияют на размер ? видимо мат.функции хоть и в двух версиях написаны (с сопроцессором и софтварно), но компилируются всегда вместе...
BlackShadow
2.07.2004 19:23
Int64 появился только в 32-битных компиляторах. По своей сути он - то же самое, что LongInt для BP: работа в EAX и EDX вместо AX и DX. Скорость сравнима... А вот Real - это либо эмуляция либо содвижок...
Atos
16.10.2004 12:13
А вот это, мне кажется, интересно: Простой пример: итерация по большому двумерному массиву может идти с совершенно разной скоростью, смотря как он обходится: горизонтально или вертикально. Как и в случае с поленом, которое легче раскалывать вдоль волокна, а не поперёк, одно направление может вызывать значительно больше отказов памяти, чем другое, а отказы обслуживаются долго. Даже программистам на ассемблере приходится делать вид, что у компьютера большая плоская память, но в системе виртуальной памяти это всего лишь абстракция, в которой при отказе памяти образуется дырка, так что отдельные обращения к памяти могут занимать значительно больше наносекунд, чем обычно.
А эту статью прочитал просто с наслаждением:Back to Basics
BlackShadow
19.10.2004 1:24
Atos, ну это и лосю понятно. При последовательном переборе ячеек примитивная оптимизация позволяет конкретно сэкономить на загрузки в регистры новых адресов.
З. Ы. : лучший способ инициализации "любомерного" массива одинаковыми значениями - это Fillchar (если меня память не подводит, то в Pascal'е эта функция называется так).
Atos
19.10.2004 10:17
Цитата(BlackShadow @ 18.10.04 18:24)
в Pascal'е эта функция называется так).
В каком именно Паскале?
Цитата
Atos, ну это и лосю понятно.
Ну, я же не лось...
Нет, в принципе, интуитивно понятно, что вроде бы на следующий элемент переходить удобнее, чем на n-й, но почему такой большой выигрыш? Тут увеличиваем смещение на единицу, там - на некоторое чиcло... Или для считывания слеующего элемента есть более быстрая команда? Хотя, наверное, я чушь порю... асм только-только начал трогать. Нельзя ли объяснить "на пальцах", как в обоих случаях будет действовать компилтор?
GoodWind
19.10.2004 16:17
Цитата
В каком именно Паскале?
Borland Turbo Pascal 7.0 TMT pascal да и во всех других, скорее всего, есть
Atos
19.10.2004 17:51
Действительно... Просто никогда её не использовал. Во всех примерах, на которых нас обучали, это делалось в цикле. Как-то прошёл мимо неё, хотя был уверен, что все основные функции помню. К тому же в описании не конкретно про массив, а просто про байты некоторой переменной говорилось. Будем знать.
Про оптимизацию я, кажется, понял. Она может достигаться применением цепочечных команд, которые позволяют процессору одновременно и работать и с регистром, и со счётчиком(как раз сегодня у нас лекция была). Так? Но, интересно, компилятор Паскаля знает, когда выгодней их использовать? Или только ассемблерными вставками?
Altair
7.11.2004 19:14
Странно, тестирую бытсрую и пирамидальную на скорость, первая бысрее... может я ошибся где-то ? Вот код:
Код
{$M 65000, 0, 0} Const N=9000; type x=array[1..N] of real; Var a:x; i:integer; T:longint; G:LongInT;
procedure swap (i, j : word); var t : real; begin t := a[i]; a[i] := a[j]; a[j] := t end;
procedure sort (n, t : word); begin while ((t shl 1+1 <= n) and (a[t shl 1+1] > a[t]) or (t shl 1 <= n) and (a[t shl 1] > a[t])) do begin if (a[t shl 1+1] >= a[t shl 1]) and (t shl 1 +1 <= n) then begin swap (t shl 1 +1, t); t := t shl 1+1 end else begin swap (t shl 1, t); t := t shl 1 end end; end; procedure Sort_Quick(var a:x; left,right:integer); var l,r:integer; B:real; begin l:=left; r:=right; B:=a[l]; repeat while (a[r]>=B) and (l<r) do r:=r-1; a[l]:=a[r]; while (a[l]<=B) and (l<r) do l:=L+1; a[r]:=a[l] until r=l; a[l]:=B; If left<L-1 then SORT_QUICK(a,left,l-1); If r+1<right then Sort_Quick(a,r+1,right); end;
begin Randomize; T:=MemL[$0040:$006C]; FOR G:=1 to 100 do begin for i := 1 to n do a[i]:=Random(MaxInt); for i := n downto 1 do sort (n, i); for i := n downto 1 do begin swap (1, i); sort (i-1, 1); end; End; T:=MemL[$0040:$006C]-T; Writeln(T); T:=MemL[$0040:$006C]; FOR G:=1 to 100 do begin for i := 1 to n do a[i]:=Random(MaxInt); Sort_Quick(A,1,n); End; T:=MemL[$0040:$006C]-T; Writeln(T);
end.
Altair
7.11.2004 20:24
Да, все верно, пирамида медленнее чем "быстрая сортировка".
Цитата
50 000 элементов: QSort - 13, HeapSort - 18 200 000: 58 и 100 соответственно
--- ЗЫ: спасибо trminator'y за помощь при проведении теста и разбор результатов.
Dark
9.11.2004 5:23
Просто память физически устроена как конденсаторы [в каком бы то не было виде] в виде матрицы, и поэтому для перехода на следующий элемент требуется изменение только RAS цикла, и перехода считывателя на несколько мм а для перехода на ячейку в другом месте RAS и CAS счетчика...
Могу путать где RAS а где CAS - но при больших объемах используемой памяти значение большое...
Как нам объясняли, кэш нужен для хранения значения ЯПамяти при обновлении ячеек...
volvo
22.01.2005 23:39
Обмен значений в переменных.
Для увеличения скорости работы программы при обмене значениями двух чисел a и b вместо конструкции
Код
{1} a := a + b; b := a - b; a := a - b;
или конструкции
Код
{2} a := a xor b; b := a xor b; a := a xor b;
желательно использовать буферную переменную:
Код
{3} T := a; a := b; b := T;
Тестируем скорость выполнения операции обмена:
Код
const n = 25000;
var tm, tm1: longint; i, j: longint; a, b, T: word; begin writeln('n = ', n, ' * ', n);
a := 1; b := 12; {----- With plus/minus -----} tm1:= MemL[$0040:$006c]; for i := 1 to n do for j := 1 to n do begin a := a + b; b := a - b; a := a - b; end; tm:= MemL[$0040:$006c]; tm:=tm-tm1; writeln('#1: ',tm);
a := 1; b := 12; {----- With xor -----} tm1:= MemL[$0040:$006c]; for i := 1 to n do for j := 1 to n do begin a := a xor b; b := a xor b; a := a xor b; end; tm:= MemL[$0040:$006c]; tm:=tm-tm1; writeln('#2: ',tm);
a := 1; b := 12; {----- With T -----} tm1:= MemL[$0040:$006c]; for i := 1 to n do for j := 1 to n do begin t := a; a := b; b := t; end; tm:= MemL[$0040:$006c]; tm:=tm-tm1; writeln('#3: ',tm); end.