Вычисления и кэш

Особенностью скриптового cad является необходимость перезапуска скрипта генерации геометрии при каждом обновлении модели. С ростом размера модели это приводит к значительному росту времени, требующегося на расчёт и отрисовку геометрии. С целью решения этой проблемы вычислительно ёмкие операции ZenCad закешированы и ленифицированы силами библиотеки evalcache.

Вместо непосредственного расчета, evalcache строит дерево построения модели на основе хэшключей генерируемых объектов. Библиотека сохраняет в кэше на жестком диске результаты операций, допускающих кэширование, и в случае, если объект уже был расчитан ранее, достаёт его из кэша. Изменение параметров при следующем запуске скрипта меняет ключи зависимых вычислений.

По умолчанию вычисления отложены (deferred): операции создают граф, а геометрия вычисляется, когда нужна для отображения, экспорта, native() или value(). Кэш помогает повторно использовать результаты одинаковых вычислений, в том числе в новом процессе.

Отладка в условиях работы с ленивыми вычислениями.

Так как evalcache выполняет вычисления только в момент, когда объект в действительности запрошен, а не тогда, когда он объявлен, могут возникать проблемы с пониманием точки возникновения возможной ошибки. Также могут возникать проблемы из-за неявного раскрытия ленивых объектов на некоторых операциях.

Для отладки и понимания точки возникновения ошибки можно в шапке скрипта включить немедленные вычисления. Публичные типы объектов при этом не меняются:

import zencad as z

z.configure(cache_enabled=False)
z.set_evaluation_mode("immediate")
body = z.box(20) - z.cylinder(3, 20)
assert isinstance(body, z.Shape)
z.set_evaluation_mode("deferred")

Режим действует до явного изменения и не меняет типы объектов. Переключение не вычисляет все уже созданные выражения. Кэш независим от режима: его отключение само по себе не отключает ленивые вычисления.

Режим cache_enabled=False cache_enabled=True
immediate Операция вычисляется сразу, без чтения и записи дискового кэша. Результат запрашивается сразу: берётся из дискового кэша, а при отсутствии вычисляется и сохраняется.
deferred Операция вычисляется, когда нужен результат, без чтения и записи дискового кэша. Когда результат понадобится, он берётся из дискового кэша, а при отсутствии вычисляется и сохраняется.

configure(cache_enabled=False) отключает чтение и запись дискового кэша, но не удаляет его файлы. Уже вычисленные результаты объектов могут повторно использоваться в памяти. Таблица описывает операции, допускающие кэширование; простые значения могут вычисляться при построении графа.

Общий дисковый кэш

По умолчанию все процессы ZenCad текущего пользователя используют один общий каталог tempfile.gettempdir()/zencad-cache-. ZenCad не удаляет его при завершении, но операционная система может очистить временный каталог.

Путь и состояние кэша можно изменить в окне настроек ZenCad. Приоритет настроек: явный configure() в процессе, затем ZENCAD_CACHE_DIR/ZENCAD_CACHE_DISABLE, затем сохранённые пользовательские настройки.

import zencad as z

z.configure(cache_dir="./model-cache", cache_enabled=True)
body = z.box(5)
print(body.mass().value())

ZENCAD_CACHE_DISABLE=1 отключает чтение и запись дискового кэша. z.clear_cache() явно очищает настроенный кэш; в обычном скрипте вызывать его не требуется.

Диагностика без редактора

zencad inspect model.py --eager --no-cache --json
zencad inspect model.py --tree
zencad inspect model.py --tree --failed-path

Заголовок скрипта может переопределить начальный режим runner. Граф показывает зависимости, cache hit и ошибки. Командная строка.

Для интеграций доступен явный владелец вычислений: context = z.Context.deferred(cache=False) и context.call(z.box, 10). У Context нет CAD-фасада; для обычных скриптов отдельный контекст не нужен. Старые настройки zencad.lazy описаны в руководстве миграции.