我正在开始编写Java库来实现高性能的有限状态机。 我知道那里有很多库,但我想从头开始编写自己的库,因为几乎所有的库都构建了自动机,这些自动机经过优化,一次只能处理一个。
我想知道SO社区中涉及状态机设计的人员在实现像这样的高性能库时,最重要/最好的设计原则是什么。
考虑
目前有关我的设计的当前问题是:
是否应定义State
,Symbol
和Transition
的课程?或者应该使用“隐藏的”内部结构。我个人认为使用类本会浪费大量内存,因为相同的信息可以以更加浓缩的形式存储。但是,这是否可以实现更快的转换?它是否有其他优点/缺点?
在内部存储数据的最佳方法是什么?使用HashMap
和HashSet
等数据结构可以实现分摊的常量时间查找,但是存在涉及开销的元素。这是最好的方法吗?将转换信息存储为原始(或非)数组似乎浪费了相当多的内存。特别是当库需要一次处理大量自动机时。不同数据结构的优缺点是什么?
我感谢任何意见。谢谢!
答案 0 :(得分:8)
你想要它有多快? brics.dk/automaton 中的代码确实声明了自己的 State 和 Transition 类,显然,这些可以使用原语重写(哎呀,整个 Transition 类的状态显然很容易适合long
)。
事情是,如果您将Transition
类移动到简单的原语,那么您不必再使用慢HashMap<Transition,...>
默认Java集合:您可以使用类似的库Trove的TLongObjectHashMap
(或TLongInt
...或TLongLong
,无论如何)拥有默认HashMap
次(Trove库基本上提供地图)当使用基元时,快速和小的超高效设置:你不会产生无数的垃圾,也不会产生基本的不必要的包裹,所以GC等等。如果你进入性能,那么你确实要检查Trove ......他们的3.0即将推出的版本比Trove 2.0快20%。
但它真的有用吗?显然,图书馆已经足够快了。毫无疑问,通过不浪费地创建对象并使用实际上表现良好的集合可以更快地制作它,但不清楚它是否可取。
除此之外,我很确定上面的库不是线程安全的。 State构造函数通过执行以下操作创建唯一ID:
static int next_id;
.
.
.
id = next_id++;
并且从... 90个不同的地方调用该构造函数!
在多线程场景中不创建唯一ID的方法的教科书示例(即使是next_id
volatile 也不够,你想要一个 AtomicInteger 这里)。我不太了解这个图书馆,但这个身份识别的东西对我来说非常很可疑。
答案 1 :(得分:3)
我有一些问题:
您需要快速完成哪个部分,FSA的输入,FSA的构建或执行 FSA?
FSA的输入来自哪里?人类是否投入了状态和弧线,还是一些自动过程?真实输入是否来自转换为FSA的正则表达式?
FSA多久可以改变一次?一秒一次?一年一次?
你知道你需要什么。除了学术图灵机之外,我从来没有见过一个重要的状态机,它不是从文本表示开始,无论是作为正则表达式还是结构化程序。
在我处理的每一个案例中,首选实现是将正则表达式直接转换为简单的结构化程序并进行编译。 没有什么能比这更快地执行。