Vlad-Shcherbina / icfpc2014-tbd

fourth place
Do What The F*ck You Want To Public License
4 stars 0 forks source link

Множественные имплементации эмуляторов. #34

Open fj128 opened 10 years ago

fj128 commented 10 years ago

У нас их было, да, но особой радости от этого не было.

Предложения:

  1. использовать IoC чтобы запускать свою фигню с произвольным интерпретатором. Типа, вместо from yole_gcc import GccMachine делать game.lambda_man_ai_from_spec (и оно не должно быть в game, если что), а дальше натуральный путь к тому, чтобы твоя фигня принимала спецификации всего через параметры.
  2. Когда делаешь интерпретатор, сразу делай его абстрактный интерфейс. Используя abc для серьёзности, ивен. Сначала в этом интерфейсе будет одна функция, "run(..)", потом другие добавятся. Минимизация количества методов приоритетна.
  3. Тесты твоего интерпретатора должны работать через абстрактный интерфейс. Если дико хочешь написать настоящий юнит-тест который тестирует конкретный метод -- ок, пиши, но не в том же месте где твои functional tests.
  4. К чему это -- после того, как несколько человек написали свои интерпретаторы и абстрактные интерфейсы к ним, можно взять и слить их интерфейсы и особенно тесты. Чтобы все интерпретаторы получали бенефит от всех тестов.
  5. И написать InterpreterMultiplexer который запускает несколько интерпретаторов одновременно и дико ругается если их результаты расходятся. При этом нужно подпатчить интерфейс чтобы он позволял вызвать one_step() и что-то вроде get_state_signature() которая в нашем случае возвращает (ip, len(data_stack), data_stack[-1] if len(data_stack) and is_int(data_stack[-1]) else 0)
  6. Premature optimization is the root of all evil, но когда речь идёт об интерпретаторах в контексте ICPFContest, чуть более чем всегда оказывается что быстрые интерпретаторы таки нужны. Поэтому когда пишешь интерфейс, думай о том как он будет применим к интерпретатору на C or C++. Premature optimization of interfaces to support more performant interpreters is not evil. It actually makes your interfaces better!

tl;dr: все должны писать интерпретаторы кроме тех которые пишут что-нибудь ещё более интересное/полезное. Но эти интерпретаторы должны представлять абстрактный интерфейс чтобы мы их могли запускать вместе и сравнивать.Тесты писать для абстрактного интерфейса (поэтому напиши его всё же сначала, но имей в виду что он наверняка поменяется). Сначала неизвестно как абстрактный интерфейс должен выглядеть, так что не заморачивайся, напиши что-нибудь и пиши код, потом поправишь.

Vlad-Shcherbina commented 10 years ago

Согласен со всеми нумерованными предложениями, но всё больше и больше сомневаюсь в принципе “все кому нечего делать должны писать эмуляторы”. Возможные преимущества большого количества эмуляторов вроде ограничиваются следующим списком:

Для первого пункта количество эмуляторов не столь важно. Если предоставлена reference implementation, то достаточно одного настоящего эмулятора и одного враппера над ней. Второй настоящий эмулятор добавит уже совсем немного (типа возможности гонять side by side на больших входах, даже если референс имплементация тормозит). Третий вообще непонятно что добавит. Ну допустим reference implementation нету или она недостаточно гибкая. В такой ситуации на мой вкус предпочтительнее иметь два хороших доделанных эмулятора, реализованных и отлаженных действительно независимо, без подглядывания в чужой код, чем скажем четыре эмулятора разной степени доделанности и заброшенности, которые вдобавок имеют общие баги.

Второй пункт вообще фикция. По сравнений со вдумчивым прочтением правил, усилия по написанию эмулятора понимание улучшают несущественно. Намного более эффективные способы проникнуться механикой - это пописать стратегий, погенерить машинный код вручную или поиграть интерактивно. Ну или тестов для существующей реализации попридумывать в крайнем случае.

Третий пункт - возможен, но представляется крайне маловероятным, так что я на него забиваю.

Четвёртый пункт полностью валиден, и я делаю вывод что писать ещё один эмулятор нужно только когда есть веская причина (например, он обещает быть в 50 раз быстрее).

Потому что множественные реализации имеют свою цену. Каждую из них нужно довести до приличного состояния и потом поддерживать. Если этого не делать, то свойство взаимозаменяемости нарушится и потеряется возможность тестирования. Но даже если они функционально равноценны, будет неразбериха с тем, какую из реализаций выбрать для использования или допиливания. Это в свою очередь может привести к тому, что у разных реализаций будут разные полезные свойства (к примеру, только YoleGcc умела считать такты, потому что это не было заложено в изначальный интерфейс), и если тебе вдруг захотелось определённую комбинацию этих свойств - tough luck. Практика показывает что при наличии нескольких реализаций одна из них становится наиболее популярной, а остальные загнивают, соответственно усилия на их разработку в значительной степени пропадают впустую. Рефакторить интерфейсы опять-таки сложнее. Плюс даже при самом красивом API, абстракции скорее всего всё равно протекут и возникнут всяческие различия, которые надо будет иметь в виду.

Как косвенный аргумент, я ещё не видел отчёта прилично выступившей команды, где бы существенно упарывались по независимым реализациям эмулятора.