Исполнитель регистровой машины считает извлеченные команды, число помещений в стек и его максимальную глубину, сохраняя результат контроллера.
Вопрос для размышления
Что могут показать счетчики уровня контроллера из того, чего не показывают одни лишь конечные значения регистров?
Подсчет каждой извлеченной команды по единому явному соглашению
Подсчет общего числа операций save без их смешивания с текущей глубиной стека
Отслеживание максимальной глубины стека при развертывании стека операциями restore
Сравнение базового и рекурсивного путей одного и того же контроллера factorial
Отличие измерений контроллера от астрономического времени и аппаратного профилирования
Для каждого запуска используется один и тот же конечный контроллер рекурсивной машины factorial. Его явный вектор регистров содержит n, val, continue, flag и pc. Отдельный список представляет стек. Исполнитель увеличивает instruction-count сразу после извлечения команды, включая команду halt. push! увеличивает total-pushes и обновляет max-depth на основе текущей длины списка, а pop! укорачивает активный стек, но не стирает накопленное количество помещений в стек.
При n, равном 1, ветвление переходит непосредственно к базовому присваиванию и достигает halt после пяти извлеченных команд без использования стека. При n, равном 3, контроллер сохраняет continue и n на двух уровнях рекурсии, выполняя четыре операции push, достигает глубины 4 и извлекает 27 команд перед остановкой со значением 6 и пустым стеком. Эти числа описывают именно этот контроллер, входные данные и соглашение о подсчете. Они не отражают затраченное время, процессорные инструкции, затраты на выделение памяти или универсальный профилировщик.
Код SICP3,860 из 1,048,576 байт UTF-8
(begin(definecontroller(vector'(testbase?n)'(branch11)'(savecontinue)'(saven)'(assignnsub1n)'(assigncontinueconstant7)'(goto0)'(restoren)'(restorecontinue)'(assignvalmultiplynval)'(goto-registercontinue)'(assignvalconstant1)'(goto-registercontinue)'(halt)))(define(register-indexname)(cond((eq?name'n)0)((eq?name'val)1)((eq?name'continue)2)((eq?name'flag)3)((eq?name'pc)4)))(define(run-factorialstart)(let((registers(vectorstart013#f0))(stack'())(instruction-count0)(total-pushes0)(max-depth0))(define(get-registername)(vector-refregisters(register-indexname)))(define(set-register!namevalue)(vector-set!registers(register-indexname)value))(define(advance!)(set-register!'pc(+(get-register'pc)1)))(define(push!value)(set!stack(consvaluestack))(set!total-pushes(+total-pushes1))(set!max-depth(maxmax-depth(lengthstack))))(define(pop!)(let((value(carstack)))(set!stack(cdrstack))value))(define(assign-valueinstruction)(let((operation(caddrinstruction)))(cond((eq?operation'constant)(cadddrinstruction))((eq?operation'sub1)(-(get-register(cadddrinstruction))1))((eq?operation'multiply)(*(get-register(cadddrinstruction))(get-register(list-refinstruction4)))))))(define(executeremaining-steps)(if(=remaining-steps0)'step-limit(let*((instruction(vector-refcontroller(get-register'pc)))(operation(carinstruction)))(set!instruction-count(+instruction-count1))(cond((eq?operation'halt)(list(get-register'val)instruction-counttotal-pushesmax-depth(lengthstack)))((eq?operation'test)(set-register!'flag(=(get-register(caddrinstruction))1))(advance!)(execute(-remaining-steps1)))((eq?operation'branch)(set-register!'pc(if(get-register'flag)(cadrinstruction)(+(get-register'pc)1)))(execute(-remaining-steps1)))((eq?operation'save)(push!(get-register(cadrinstruction)))(advance!)(execute(-remaining-steps1)))((eq?operation'restore)(set-register!(cadrinstruction)(pop!))(advance!)(execute(-remaining-steps1)))((eq?operation'assign)(set-register!(cadrinstruction)(assign-valueinstruction))(advance!)(execute(-remaining-steps1)))((eq?operation'goto)(set-register!'pc(cadrinstruction))(execute(-remaining-steps1)))((eq?operation'goto-register)(set-register!'pc(get-register(cadrinstruction)))(execute(-remaining-steps1)))))))(execute200))); value, fetched instructions, pushes, max depth, final depth(run-factorial3))
Примеры
Результат—
Вывод
—
Значение
—
Диагностика
—
Трасса выполнения0 / 0 событий
Ожидаемый результат
Первая программа возвращает (6 27 4 4 0). Вторая возвращает ((1 1 5 0 0) (2 2 16 2 2) (4 24 38 6 6)).
На что обратить внимание в трассе
Следите за instruction-count сразу после каждого извлечения из вектора, включая команду halt. Сопоставляйте каждый save с увеличением счетчика push и возможным обновлением max-depth, затем наблюдайте, как restore укорачивает текущий стек, не уменьшая total-pushes. Базовый путь переходит непосредственно к команде 11, тогда как каждый дополнительный уровень рекурсии добавляет два save и затем два restore.
Попробуйте сами
Измените программу и сравните результат.
Предскажите отчет для входного значения 3 без запуска программы. Затем измените соглашение о подсчете так, чтобы halt не учитывалась, и объясните, какое именно поле изменится.
Показать подсказку
Входное значение 3 создает два уровня рекурсии. Каждый уровень помещает в стек continue и n. Исключение halt вычитает единицу только из числа извлеченных команд.