OberonCore https://forum.oberoncore.ru/ |
|
Быстр-е/оптимизация поиска в матрице https://forum.oberoncore.ru/viewtopic.php?f=2&t=2357 |
Страница 1 из 2 |
Автор: | Александр Ильин [ Вторник, 16 Февраль, 2010 00:11 ] |
Заголовок сообщения: | Быстр-е/оптимизация поиска в матрице |
Выделено: viewtopic.php?p=42858#p42858 Интереса ради сравнил скорость выполнения трёх написанных мною примеров. Естественно, единица измерения - секунды, делённые на сферических попугаев в вакууме (сек/спв). Взята матрица arr: ARRAY 1000, 1000 OF INTEGER (16-битные слова в XDS). Во все элементы помещены значения 999, и только последний элемент arr[999, 999] := 1000. Соответственно, сделан поиск значения 1000 тремя вышеприведенными программами по 1000 раз (взято минимальное значение времени из 11 запусков подряд). Результаты замеров (чем меньше, тем лучше): - один WHILE: 13.635 сек/спв; - два вложенных WHILE: 6.804 сек/спв; - с локальной процедурой: 2.422 сек/спв. WIN! Потом подумал: "Ой, чего это я в отладочном режиме проверяю?!" Включил всю оптимизацию, отключил все рантайм-проверки и запустил снова. Результаты (чем меньше, тем лучше): - один WHILE: 2.376 сек/спв; - два вложенных WHILE: 1.279 сек/спв; - с локальной процедурой: 0.813 сек/спв; WIN! Прирост производительности за счёт оптимизации составил (чем меньше, тем лучше): - один WHILE: 13.635/2.376=5.7386; - два вложенных WHILE: 6.804/1.279=5.3198; - с локальной процедурой: 2.422/0.813=2.9791. WIN! Получается, что поиск с использованием локальной процедуры выигрывает по всем статьям. В случае с включенной оптимизацией он в полтора раза быстрее двух вложенных WHILE (из-за вынужденных лишних IF-ов внутри, которые тормозят работу), и в три раза быстрее единственного WHILE. В случае с отключенной оптимизацией, то есть, когда всякие проверки индексов выполняются в полном объёме, он почти в три раза быстрее двух WHILE и в пять с половиной раз быстрее одного WHILE. При этом состоит из простейших компонентов - паттерновых линейных поисков - и увеличивает читабельность, давая процедурное имя внутреннему циклу. Как говорится, EPIC WIN. Два вложенных WHILE вдвое быстрее одного WHILE, что тоже интересно. : ) Если кому-то хочется, могу выложить исходники проекта на XDS. |
Автор: | Alexey_Donskoy [ Вторник, 16 Февраль, 2010 09:05 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Александр Ильин писал(а): Получается, что поиск с использованием локальной процедуры выигрывает по всем статьям. В случае с включенной оптимизацией он в полтора раза быстрее двух вложенных WHILE (из-за вынужденных лишних IF-ов внутри, которые тормозят работу), и в три раза быстрее единственного WHILE. В случае с отключенной оптимизацией, то есть, когда всякие проверки индексов выполняются в полном объёме, он почти в три раза быстрее двух WHILE и в пять с половиной раз быстрее одного WHILE. Тестирование некорректно, как и анализ результатов.1) В принципе вариант с процедурой не может быть эффективнее двух вложенных циклов (из-за накладных расходов на вызов процедуры). Максимум, чего можно добиться - оптимизирующий компилятор подставит процедуру inline, и будут те же два вложенных цикла. 2) В данном конкретном примере главную оптимизацию выполнил сам программист - а именно, сделал разновидность хака (которая в разных компиляторах может выглядеть совершенно по-разному, а то и не реализуема вообще) - во вложенном цикле работает не с двумерным массивом, а с одномерным (естественно, экономится умножение на каждом внутреннем цикле. Или, если компилятор это умеет, одно косвенное обращение к массиву ссылок на начала строк двумерного массива). Вывод: применённый хак имеет мало отношения к алгоритмизации и программированию, а чисто основан на использовании фич конкретного компилятора конкретного языка. |
Автор: | Сергей Губанов [ Вторник, 16 Февраль, 2010 12:02 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Александр Ильин писал(а): Получается, что поиск с использованием локальной процедуры выигрывает по всем статьям. Я тоже проверил в ББ. Сравнивал вариант со вложенной процедурой с симуляцией цикла Дейкстры с помощью LOOP.При размере матрицы 1000*1000 вложенная процедура победила с отрывом в 28%, но при размере матрицы 100*100 победил цикл Дейкстры с отрывом в 53% (для матрицы 32*32 цикл Дейкстры победил с отрывом уже в 92%). В общем, для больших матриц рулит вложенная процедура, для малых - симуляция цикла Дейкстры с помощью LOOP. Интересно было бы ещё проверить в Oberon-07 в котором цикл Дейкстры "родной". Там, по идее, если компилятор не сглупит, ЦД должен бы побеждать и на больших матрицах тоже. Код: MODULE TestSearch2D;
IMPORT Log := StdLog, Services; CONST N = 1000; cpuClock = 2.21; (* частота моего процессора в гигагерцах *) VAR a: ARRAY 1000, 1000 OF INTEGER; i, j, v, n: INTEGER; t: LONGINT; dt: REAL; PROCEDURE FindInLine (IN a: ARRAY OF INTEGER; OUT pos: INTEGER): BOOLEAN; VAR i: INTEGER; BEGIN i := 0; WHILE (i < LEN(a)) & ~(a[i] = v) DO INC(i) END; pos := i; RETURN (i < LEN(a)) END FindInLine; BEGIN FOR i := 0 TO LEN(a, 0) -1 DO FOR j := 0 TO LEN(a, 1) -1 DO a[i, j] := 999 END END; v := 1000; a[LEN(a, 0)-1, LEN(a, 1)-1] := v; (************************************) t := Services.Ticks(); FOR n := 1 TO N DO i := 0; j := 0; LOOP IF (j < LEN(a, 1)) & ~(a[i, j] = v) THEN INC(j) ELSIF (j = LEN(a, 1)) & (i < LEN(a, 0)) THEN j := 0; INC(i) ELSE EXIT END END; END; dt := Services.Ticks() - t; Log.String("Симуляция цикла Дейкстры:"); Log.Ln; Log.String("i = "); Log.Int(i); Log.Ln; Log.String("j = "); Log.Int(j); Log.Ln; Log.Real(dt); Log.String(" секунд на весь тест"); Log.Ln; Log.Real(dt / N * 1000); Log.String(" микросекунд на один поиск"); Log.Ln; Log.Real(dt / N * 1000000 * cpuClock / (LEN(a, 0)*LEN(a, 1))); Log.String(" тактов на элемент"); Log.Ln; (************************************) t := Services.Ticks(); FOR n := 1 TO N DO i := 0; j := 0; WHILE (i < LEN(a, 0)) & ~FindInLine(a[i], j) DO INC(i) END END; dt := Services.Ticks() - t; Log.String("Использование процедуры:"); Log.Ln; Log.String("i = "); Log.Int(i); Log.Ln; Log.String("j = "); Log.Int(j); Log.Ln; Log.Real(dt); Log.String(" секунд на весь тест"); Log.Ln; Log.Real(dt / N * 1000); Log.String(" микросекунд на один поиск"); Log.Ln; Log.Real(dt / N * 1000000 * cpuClock / (LEN(a, 0)*LEN(a, 1))); Log.String(" тактов на элемент"); Log.Ln END TestSearch2D. |
Автор: | Alexey_Donskoy [ Вторник, 16 Февраль, 2010 12:09 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Друзья, ну некорректно же с такой процедурой сравнивать! viewtopic.php?p=42887#p42887 Перепишите тогда два цикла с такой же оптимизацией, как в случае процедуры! |
Автор: | Info21 [ Вторник, 16 Февраль, 2010 12:21 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Сергей Губанов писал(а): проверил в ББ. Сравнивал вариант со вложенной процедурой с симуляцией цикла Дейкстры с помощью LOOP. Не должен. Только граница, где процедура начинает обгонять, могла бы сдвинуться (см. ранее), и то вряд ли (цикл Дейкстры никак нельзя реализовать по особенному по сравнению с LOOP IF ELSIF ELSE EXIT END END).
При размере матрицы 1000*1000 вложенная процедура победила с отрывом в 28%, но при размере матрицы 100*100 победил цикл Дейкстры с отрывом в 53% (для матрицы 32*32 цикл Дейкстры победил с отрывом уже в 92%). В общем, для больших матриц рулит вложенная процедура, для малых - симуляция цикла Дейкстры с помощью LOOP. Интересно было бы ещё проверить в Oberon-07 в котором цикл Дейкстры "родной". Там, по идее, если компилятор не сглупит, ЦД должен бы побеждать и на больших матрицах тоже. |
Автор: | Виктор О [ Вторник, 16 Февраль, 2010 12:43 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Тогда до кучи алгоритм от партизана ![]() Код: VAR
arr: ARRAY N, M OF INTEGER; i, k: INTEGER; find,found: BOOLEAN; BEGIN i := 0; k := 0; find := TRUE; WHILE find DO found := arr [i, k] = x; IF found THEN find:=FALSE; ELSEIF i<N-1 THEN INC (i) ELSEIF k<M-1 THEN INC (k); i:=0 ELSE find:=FALSE END END END |
Автор: | Сергей Губанов [ Вторник, 16 Февраль, 2010 12:46 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Info21 писал(а): Не должен. Только граница, где процедура начинает обгонять, могла бы сдвинуться (см. ранее), и то вряд ли (цикл Дейкстры никак нельзя реализовать по особенному по сравнению с LOOP IF ELSIF ELSE EXIT END END). Вот ЦД:WHILE (j < LEN(a, 1)) & ~(a[i, j] = v) DO INC(j) ELSIF (j = LEN(a, 1)) & (i < LEN(a, 0)) DO j := 0; INC(i) END Обратим внимание на первую строчку WHILE (j < LEN(a, 1)) & ~(a[i, j] = v) DO INC(j) Если компилятор не сглупит, то он заменит её на такую: WHILE (j < LEN(b)) & ~(b[j] = v) DO INC(j) где "b = a[i]". И тогда ЦД будет выполняться не медленнее чем в случае со вложенной процедурой, так как именно этот цикл написан в её теле. |
Автор: | Сергей Губанов [ Вторник, 16 Февраль, 2010 13:47 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Придумал ещё один вариант: Код: i := -1; REPEAT j := 0; INC(i); IF i < LEN(a, 0) THEN WHILE (j < LEN(a, 1)) & ~(a[i, j] = v) DO INC(j) END; END UNTIL j < LEN(a, 1); IF i < LEN(a, 0) THEN нашли ELSE не нашли END Он чуть-чуть обгоняет симуляцию ЦД на больших матрицах, но отстаёт на малых. Компилятор упорно тупит не желая оптимизировать строку WHILE (j < LEN(a, 1)) & ~(a[i, j] = v) DO INC(j) END; до строки WHILE (j < LEN(b)) & ~(b[j] = v) DO INC(j) END; где "b = a[i]". |
Автор: | Александр Ильин [ Вторник, 16 Февраль, 2010 13:56 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Сергей, раз уж вы этим занимаетесь, проверьте, пожалуйста, влияет ли на быстродействие замена LEN(a) на соответствующую константу (N либо M)? |
Автор: | Peter Almazov [ Вторник, 16 Февраль, 2010 14:01 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
У Вас LEN(a) меняется внутри цикла что ли? Зачем ее каждый раз вычислять? UPD: опоздал ![]() |
Автор: | Info21 [ Вторник, 16 Февраль, 2010 16:04 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Peter Almazov писал(а): У Вас LEN(a) меняется внутри цикла что ли? Зачем ее каждый раз вычислять? А она не вычисляется, а берется.
|
Автор: | Info21 [ Вторник, 16 Февраль, 2010 16:18 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Сергей Губанов писал(а): Вот ЦД: Да, это тоже цикл Дейкстры. Только мой лучше ![]() |
Автор: | Сергей Губанов [ Вторник, 16 Февраль, 2010 17:02 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Если тип массива объявлен с фиксированным размером (что тут и сделано), то LEN(a) - константа подставляемая во время компиляции. Если тип массива объявлен открытым, то LEN(a) - переменная доступная только для чтения. Вводить вместо неё другую переменную (которая к тому же ещё и будет доступна для записи) смысла нет (или даже вредно). |
Автор: | Сергей Губанов [ Вторник, 16 Февраль, 2010 17:06 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Info21 писал(а): Только мой лучше А мой последний вариант с REPEAT-ом ещё лучше ![]() ![]() |
Автор: | Info21 [ Вторник, 16 Февраль, 2010 18:41 ] |
Заголовок сообщения: | Re: Базовые паттерны циклов |
Сергей Губанов писал(а): Info21 писал(а): Только мой лучше А мой последний вариант с REPEAT-ом ещё лучше ![]() ![]() |
Автор: | QWERTYProgrammer [ Среда, 17 Февраль, 2010 00:09 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Сергей Губанов писал(а): Компилятор упорно тупит не желая оптимизировать строку WHILE (j < LEN(a, 1)) & ~(a[i, j] = v) DO INC(j) END; до строки WHILE (j < LEN(b)) & ~(b[j] = v) DO INC(j) END; где "b = a[i]". Так вроде компилятор в ББ неоптимизирующий? |
Автор: | Info21 [ Среда, 17 Февраль, 2010 07:30 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
QWERTYProgrammer писал(а): Так вроде компилятор в ББ неоптимизирующий? И слава богу! (Свят, свят, свят!...) |
Автор: | Сергей Губанов [ Среда, 17 Февраль, 2010 11:36 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
QWERTYProgrammer писал(а): Так вроде компилятор в ББ неоптимизирующий? Смотря что под этим понимать. Программы ББ работают удовлетворительно шустро. В то же самое время если, например, в MS VS C# снять галку "оптимизация", то тормоза будут ещё те.Кстати, сравнивая с C# обнаружил следующее различие. Если в Обероне объявлена процедура PROCEDURE f (VAR line: ARRAY OF INTEGER); и объявлена переменная VAR a: ARRAY 10, 10 OF INTEGER; то в ту процедуру можно засунуть строку матрицы: f(a[2]). А в C# этого сделать нельзя! void f (int[] x) int[,] a = new int[10, 10]; f(a[2]); <-- низя!!! То есть самый быстрый выриант с процедурой (в которой осуществлялся проход по строке) в C# нереализуем!!! |
Автор: | Виктор О [ Среда, 17 Февраль, 2010 11:51 ] |
Заголовок сообщения: | Re: Быстр-е/оптимизация поиска в матрице |
А где же Свидетель Си с алгоритмом на низкоуровневых массивах - в одну строчку и пустым телом цикла? Нехорошо-с, совсем распугали оппонентов ![]() |
Автор: | Info21 [ Среда, 17 Февраль, 2010 12:28 ] |
Заголовок сообщения: | Re: Поиск элемента матрицы |
Сергей Губанов писал(а): то в ту процедуру можно засунуть строку матрицы: f(a[2]). А в C# этого сделать нельзя! Любопытно. Чем же они думали.Эта вещь нередко встречается при работе с матрицами и т.п. Естественная такая вещь. Может быть, не вещь первого плана, но всё-таки показатель качества проектирования -- ортогональность конструкций. |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |