![]() |
|
|
Регистрация Восстановить пароль |
Регистрация | Задать вопрос |
Заплачу за решение |
Новые сообщения |
Сообщения за день |
Расширенный поиск |
Правила |
Всё прочитано |
![]() |
|
|
Опции темы
![]() |
Поиск в этой теме
![]() |
![]() |
#1 |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]()
Глобальные переменные:
Код:
Код:
В первую очередь интересует именно ускорение этого алгоритма, а не вариант другого. Но если у кого-то появятся варианты, то вот описание: Функция двигает фишки на квадратном поле 16х16 (poleSwork) пока не останется одна фишка. Граница, через которую фишки двигать нельзя - poleSg. Все сделанные ходы от начальной расстановки до текущей хранятся в массиве wayS. В нём wayS[XXX].p - позиция подвинутой фишки, а wayS[XXX].n - направление сдвига. Фишки двигаются только перескоком через другую фишку, при этом перепрыгнутая фишка удаляется. Счётчик MainCounter нужен чисто на время отладки. Прерывание работы функции по флагу isRunning. Последний раз редактировалось alcaedo; 05.09.2015 в 23:43. Причина: серая раскраска кода после точки с зяпятой в строке |
![]() |
![]() |
![]() |
#2 |
МегаМодератор
СуперМодератор
Регистрация: 09.11.2010
Сообщений: 7,429
|
![]()
Во-первых, сократим-ка длину кода:
Код:
В-третьих, из-за ленивых вычислений лучше выстроить условия в самом длинном ифе так, чтобы проверка при отрицательном исходе прекращалась как можно раньше (но я не смог решить, какое из условий "отрицательно заряжено" ![]() В-четвертых, не совсем понял, что хранится в poleSg. В-пятых, можно произвести профилирование кода, для поиска узких мест (http://www.gunsmoker.ru/2013/01/opti...on-basics.html). Предложить другой алгоритм не могу - спать хочется ![]()
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись
![]() |
![]() |
![]() |
![]() |
#3 |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]()
1) Оно примерно так и было изначально, специально развернул цикл и избавился от лишних процедур
2) я бы отказаться от типа integer в пользу byte, но там диапазон возможных значений от -32 до +287. И ещё вот по поводу типа этих переменных такая непонятка: fnp,fep:smallint; {скорость 2760 KOps/s } fnp,fep:integer; {скорость 3085 KOps/s } fnp,fep:int64; {скорость 3075 KOps/s } Скорости указаны на момент, когда функция ещё работала в главном потоке программы. 3) Это я подумаю. Так сходу сложно сказать какое из них вначале выставить. Но смысл понятен. 4) В poleSg - граница, через которую фишки двигать нельзя и на которую двигать нельзя. Работает как "исключающая маска" на poleSwork. На картинке красные квадратики - это poleSg, синие - poleS. ![]() Протестировал. Сейчас, работая в отдельном потоке, вариант с циклом выдаёт скорость 2630 килоопераций в секунду, без цикла (как в первом посте) 3270. Как-то бы ещё распараллелить по четырём направлениям, а то четырёхядерный проц загружен на 25%. Последний раз редактировалось Stilet; 06.09.2015 в 10:19. |
![]() |
![]() |
![]() |
#4 | |
Старожил
Регистрация: 20.04.2008
Сообщений: 5,542
|
![]() Цитата:
Код:
Далее каждое их ЭТИХ четырех состояний (четыре КОПИИ!!!) анализировать независимо, в потоках. (той же самой WORKS но все глобальные переменные сделать ЛИЧНЫМИ данными потока (те самые КОПИИ!!). недостаток. любой поток может закончится "слишком рано" если ему попадутся "плохие" данные. Хорошо бы в таком случае потоку иметь возможность "отобрать"(попросить) себе новое исходное состояние у других потоков.
программа — запись алгоритма на языке понятном транслятору
|
|
![]() |
![]() |
![]() |
#5 |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]()
Проблема в том, что чаще всего первые 8-9 вариантов хода тупиковые
Вот это и хотелось бы, но пока не знаю, как |
![]() |
![]() |
![]() |
#6 |
Старожил
Регистрация: 20.04.2008
Сообщений: 5,542
|
![]()
как вариант с бесконечным числом потоков
каждый поток создает себе четыре новых потока для проверки СВОИХ под-деревьев и спокойно засыпает ДО получения результатов от своих субпотоков. бесконечное число потоков переделываем в пул потоков. при старте потока заносим в очередь четыре новых узла для проверки и ждем засыпаем до результатов проверки. Рекурисию вызовов Works превращаем в очередь проверок ТЕКУЩЕГО состояния. замена обхода в глубину(проверка сразу на всю глубину ветви) на обход в ширину (проверяем "сразу все ветки" но только на один шаг в глубину) Но очередь получится длинная (при этом надо будет хранить много-много текущих состояний) хотя и будет идти быстро. Хотя текущее состояние легко вычисляется по исходному при наличии "записи" ходов. вариант совместить обход в ширину с обходом глубину. 1. пока очередь маленькая делаем обход в ширину (наращиваем очередь) 2. при достижения "критической" длины начинаем проводить анализ до конца. переходим на анализ в глубину) 3. как только какой либо "глубокий" анализ заканчивается (т.е. уменьшается очередь) снова пробуем наращивать очередь (п.1)
программа — запись алгоритма на языке понятном транслятору
|
![]() |
![]() |
![]() |
#7 |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]()
Распараллелил. Программа с формой целиком: BG.zip
Тут всплыла одна проблема. Несколько ходов в разной последовательности могут приводить из одного начального состоя в одинаковое конечное. Программа посчитает такие последовательности ходов разными и будет обрабатывать все варианты (либо одновременно в разных потоках, либо по очереди в одном потоке). То есть, программа выполняет бесполезную работу. Ведь, если мы приходим к состоянию, которое уже обрабатывалось или которое сейчас обрабатывается в соседнем потоке, то можно его откинуть. Как вы думаете, какой способ отсева будет самым не затратным? ![]() ps: не нашёл, как вставить гифку в спойлер Последний раз редактировалось alcaedo; 22.09.2015 в 09:41. |
![]() |
![]() |
![]() |
#8 | ||
Старожил
Регистрация: 20.04.2008
Сообщений: 5,542
|
![]()
Если мы не хотим повторно обрабатывать, то надо помнить ВСЕ промежуточные состояния.
1) работать только с очередью.(только анализ в ширину) 2) при добавлении проверять наличие "дубликата". (для ускорения поиска дубликата нужно иметь упорядоченный каким-либо образом список, но упорядочивание снижает скорость добавления). 3) после анализа НЕ удалять из очереди а помечать как обработанный. В конце получаем очень большую очередь включающую ВСЕ допустимые состояния. ( а хватит ли памяти!) возврат к "нормальной"очереди проверок. можно ПОМНИТЬ все проверенные маршруты (последовательности ходов, не состояния). Цитата:
после добавления новых сгенерированных маршрутов, исходный маршрут исключается из очереди. Мы его в любом случае получим (и даже не один раз) при восстановлении из дочерних маршрутов. НУ и комбинация приемов.(восстанавливаем анализ в глубину) При каждом СПУСКЕ (проверяем нашу очередь на дубликаты) и либо возвращаемся либо регистрируем новый обработанный путь и ... Хотя ЗДЕСЬ надо внимательно "смотреть за синхронизацией". Один начал но не успел добавить, и тут же второй пришел к этому. посмотрел код Цитата:
по хорошему поток НИЧЕГО не должен знать о других потоках. Максимум с кем он должен общаться это головной поток, поддерживающий очередь. отдать / не отдать должен решать не сам поток, проверяя данные других потоков, а головной поток ЗНАЮЩИЙ сколько у него обрабатывающих(4, 8 вообще один), и чем они в данный момент заняты. Представьте себе у вас появился 8-ядерный процессор, вы захотели увеличить число потоков обработки, и ... полная (капитальная) переделка ВСЕХ потоковых процедур. Или же внесения изменений в список обрабатывающих (увеличение списка запускаемых потоков и всЁ) Все обрабатывающие потоки должны быть ОДИНАКОВЫ. т.е. не четыре класса с РАЗНЫМИ процедурами обработки и разными ГЛОБАЛЬНЫМИ данными а ОДИН класс в котором КАЖДЫЙ экземпляр имеет СВОИ ЛОКАЛЬНЫЕ данные. ГЛОБАЛЬНЫМ по хорошему счету должен быть МАССИВ ЭКЗЕМПЛЯРОВ рабочего потока. и ОДИН экземпляр управляющего потока (он может и не быть, если управляющим потоком будет выступать основной). ВСЕ остальные данные (если это не обще программные константы) должны быть локальными ПОЛЯМИ потока.
программа — запись алгоритма на языке понятном транслятору
Последний раз редактировалось Stilet; 22.09.2015 в 18:56. |
||
![]() |
![]() |
![]() |
#9 | |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]() Цитата:
Сейчас это выглядит так: ![]() Главное, чтобы поток получал задания только из одного места. И не важно, из какого. Ниже - ещё несколько безопасных вариантов: 1) ![]() 2) ![]() 3) ![]() Последний раз редактировалось alcaedo; 22.09.2015 в 23:54. |
|
![]() |
![]() |
![]() |
#10 | |
Пользователь
Регистрация: 05.09.2015
Сообщений: 28
|
![]() Цитата:
- внутри функций NWSx3 заменить по 4 упоминания "z0" на "z4" - создать пару новых функций NWS24 и NWS34, в которых в тех же 4-х местах будет стоять "z0", а первой строкой стоит "begin with z4 do" - ещё, конечно, надо добавлять переменную z4 и "procedure TMyWorkerThread4.Execute;", но это не имеет отношения к капитальной переделке Ну, они почти одинаковы. У каждого есть управляющий и подчинённый. Не очень понимаю, как VCL-поток будет брать текущее состояние из локальных данных рабочих потоков Вообще, я не могу придумать ситуацию, в которой глобальные данные могли бы быть повреждены или прочитаны неверно в этой программе (не считая момента, когда VCL читает, например, переменную счётчика, а в этот же момент какой-то поток делает запись в эту переменную, что не критично). В каждой ситуации, где к одной переменной могут обратиться несколько потоков, во-первых, есть флаг JNR, который управляющий поток может только менять на FALSE, а управляемый только менять на TRUE. Общающиеся потоки меняют флаг только тогда, когда в них закончилась обработка глобальных переменных zx. Во-вторых, у каждого управляемого потока есть только 1 управляющий. Это значит, что если управляющий поток видит JNR=TRUE и начинает работать с переменной z, то никакой другой поток в это время с переменной z не работает. Просто нет другого управляющего потока. А управляемый не работает с z, т.к. ждёт JNR=FALSE. Сами переменные JNR занимают в памяти 1 байт, а значит запись и чтение в них происходит одной операцией. Ситуации, когда управляемый поток пытается записать JNR=TRUE, но приостанавливается на середине, а потом управляющий пытается прочесть недозаписанные данные, не может возникнуть. С VCL-потоком сложнее. Тут может возникнуть ситуация чтения недозаписанных данных (счётчики, переменные zx.w), но это не может привести к ошибке, а только в временному (до следующего тика таймера) искажению информации на форме. Переменные zx.isStartTh и zx.isFinishTh, которые читает VCL-поток по таймеру, а выставить может какой-то рабочий поток, тоже однобайтовые. Они прочитаются правильно. Переменная isRunning может быть изменена и рабочим и VCL-потоком. Возможна ситуация, когда одновременно её пытаются изменить из нескольких мест, но тут фишка в том, что эта однобайтовая переменная может меняться только с TRUE на FALSE. Даже, если все пять потоков программы будут записывать isRunning:=FALSE, всё-равно в результате получим FALSE. UPD: всё-таки, чтение VCL-потоком переменной zx.w может вызвать ошибку в процедуре pole_upd вот тут: Код:
Последний раз редактировалось alcaedo; 23.09.2015 в 00:57. |
|
![]() |
![]() |
![]() |
|
![]() |
||||
Тема | Автор | Раздел | Ответов | Последнее сообщение |
W1050 WideChar reduced to byte char in set expressions. | Человек_Борща | Общие вопросы Delphi | 8 | 19.06.2012 20:57 |
Ошибки в программе - функция для работы с множествами | X-REY | Паскаль, Turbo Pascal, PascalABC.NET | 4 | 26.10.2011 20:48 |
Оптимизация методов работы с БД | Lindemann66 | C/C++ Базы данных | 1 | 11.10.2011 13:06 |
W1050 WideChar reduced to byte char in set expressions. Что делать? | SkAndrew | Общие вопросы Delphi | 3 | 01.11.2008 07:51 |
Модуль для работы с множествами [Pascal] | iFool | Помощь студентам | 2 | 20.10.2008 22:04 |