Проектирование высокопроизводительной машины State в Java
Я начинаю писать библиотеку Java для создания высокопроизводительных конечных машин. Я знаю, что есть много библиотек, но я хочу писать свои собственные с нуля, так как почти все библиотеки там построили автоматики, оптимизированные для обработки только по одному.
Я хотел бы знать, что люди из сообщества SO, которые занимались дизайном государственного аппарата, являются наиболее важными/лучшими принципами дизайна, когда речь заходит о реализации высокопроизводительных библиотек, подобных этим.
Вопросы
- Созданные автоматы обычно не массивные. (~ 100-500 состояний).
- Реализация должна иметь возможность масштабировать.
- Реализация должна включать быстрые преобразования (минимизация, детерминация и т.д.).
- Глядя на внедрение DFA, NFA, GNFA, PDA и, возможно, Tree Automata. Надеюсь, под одним интерфейсом, если это возможно.
- Должен иметь хороший баланс между использованием памяти и производительностью.
Текущие вопросы, касающиеся дизайна для меня на данный момент:
-
Должны ли быть определены классы для State
, Symbol
и Transition
? Или должна быть использована "скрытая" внутренняя структура. Лично я чувствую, что использование классов как таковых будет тратить много памяти, поскольку одна и та же информация может храниться в гораздо более сжатой форме. Но позволяет ли это более быстрое преобразование? Имеет ли он какие-либо другие плюсы/минусы?
-
Каким будет наилучший способ хранения данных внутри? Использование структур данных, таких как HashMap
и HashSet
, позволяет отслеживать постоянный поиск по времени, но есть элемент накладных расходов. Это лучший способ? Сохранение информации о переходе в виде примитивного (или нет) массива, похоже, отнимает немало памяти. Особенно, когда библиотека должна обрабатывать множество автоматов одновременно. Каковы плюсы и минусы различных структур данных?
Я ценю любой вход. Спасибо!
Ответы
Ответ 1
Хорошо, как быстро вы это хотите? Код в файле brics.dk/automaton объявляет собственные классы состояния и перехода, очевидно, их можно было бы переписать с помощью примитивов (черт, все состояние класса перехода, по-видимому, легко поместилось бы на long
).
Вещь, если вы перемещаете, например, класс Transition
в просто примитив, то вы не должны больше использовать медленные коллекции HashMap<Transition,...>
по умолчанию Java: вы можете использовать библиотеки, такие как Trove TLongObjectHashMap
(или TLongInt
... или TLongLong
, что угодно), который владеет по умолчанию HashMap
в больших количествах (библиотеки Trove в основном предоставляют карты и наборы, которые являются суперэффективными, быстрыми и малыми, когда вы работаете с примитивами: вы не генерируете бесчисленное количество мусора или постоянную ненужную упаковку вокруг примитивов, так что меньше GC и т.д. Если вы входите в производительность, то вы хотите проверить Trove... И их 3.0 предстоящий выпуск составляет 20% быстрее, чем Trove 2.0).
Но действительно ли это полезно? По-видимому, эта библиотека уже очень быстро. Нет сомнений, что это можно сделать быстрее, не расточительно создавая объекты и используя коллекции, которые действительно хорошо работают, но не ясно, что это было бы желательно.
Кроме того, я уверен, что библиотека выше не является потокобезопасной. Конструктор состояний создает уникальный идентификатор, делая это:
static int next_id;
.
.
.
id = next_id++;
и этот конструктор вызывается из... 90 разных мест!
Пример учебника о способе не создать уникальный идентификатор в многопоточном сценарии (черт, даже сделать next_id
volatile не хватит, вы хотите, например, AtomicInteger здесь). Я недостаточно хорошо разбираюсь в библиотеке, но эта идентификационная вещь выглядит очень подозрительной для меня.
Ответ 2
У меня есть несколько вопросов:
-
В какой части вам нужно быть быстрым, ввод FSA, создание FSA или выполнение FSA?
-
Откуда берется вход FSA? Человек вводит состояния и дуги, или какой-то автоматический процесс? Является ли реальный ввод исходным выражением, преобразованным в FSA?
-
Как часто меняется FSA? Раз в секунду? Один раз в год?
Вы знаете, что вам нужно. Помимо академических машин Тьюринга, я никогда не видел значительную машину состояний, которая не начиналась с текстового представления, либо как регулярное выражение, либо структурированная программа.
В каждом случае, с которым я столкнулся, предпочтительной реализацией было преобразование регулярного выражения непосредственно в простую структурированную программу и ее компиляцию.
Ничто не будет выполняться быстрее, чем это.