我已经写了一个prolog程序,现在我正在尝试优化它的性能(是的,这个用例实际上需要它)。
首先介绍一下程序最初的结构背景,以便了解我的目标。
系统将客户(用户)订单保存在逻辑库中,这些订单在它们进入时动态地声明到逻辑库中(并且一旦使用撤消处理就动态删除)。最初的订单结构如下:
order(RegionID, UserID, UserBalance, OrderID, ProductID, Price, ...) .
order(RegionID, UserID, UserBalance, OrderID, ProductID, Price, ...) .
...
order(RegionID, UserID, UserBalance, OrderID, ProductID, Price, ...) .
我很喜欢这个,但是在测试过程中,我用50,000个订单填充了系统,发现处理过程花了很长时间(大约几分钟 - 它需要更好)。我描述并发现花了大部分时间花在逻辑基础上挖掘订单进行处理,所以决定尝试另一种方案。
这是有道理的,因为特定用户与特定区域相关联:
order(RegionID, [ (UserID, UserBalance, OrderID, ProductID, Price, ...), (UserID, UserBalance, OrderID, ProductID, Price, ...), ...]) .
order(RegionID, [ (UserID, UserBalance, OrderID, ProductID, Price, ...), (UserID, UserBalance, OrderID, ProductID, Price, ...), ...]) .
...
order(RegionID, [ (UserID, UserBalance, OrderID, ProductID, Price, ...), (UserID, UserBalance, OrderID, ProductID, Price, ...), ...]) .
我在这里做的是为每个地区存储一长串用户订单。为了测试这一点,我在订单结构中制作了50,000个长度(50,000个订单)的列表。在处理订单时,这比原始方案表现要好得多(25% - 原始时间的30%);但是,在向系统添加订单时,如果不是更多,它的执行情况至少会降低一个数量级。
订单添加程序非常简单。我只是通过实例化RegionID来收回订单结构,然后再添加一个额外的订单添加到头部(类似这样):
retract( order(california, OldOrders ) ).
assert ( order(california, [ NewOrder | OldOrders ] ) ).
我会认为这是相当快的,因为我只是在头上添加一些东西,但事实并非如此。我的猜测是在幕后有很多复制的长列表。
我的问题只是如何针对速度进行更优化。你可能会建议一个不同的数据结构,一个不同的算法,一个不同的机制来存储这些东西(我只知道断言/撤回,但不同的prologs可能有更多的奇异机制?),或任何你想要的。请记住,有任何建议,我不想在订单处理(与添加)相反的情况下倒退。
我目前正在使用Eclipse(prolog,而不是IDE),但如果您的建议需要,我可以轻松切换到XSB,yap或任何其他免费序言。请注意,我们需要坚持更快的序言而不是像SWI那样较慢的序言。
感谢您的任何建议。
答案 0 :(得分:1)
我认为您的主要问题是您没有从索引中受益,因为您的订单处理查询以实例化的用户ID开头,而不是该术语的第一个参数。可能你正在进行一个查询,其中有两个或三个参数被实例化,但Prolog只能根据参数顺序将order/N
的实例转移到具有相同区域ID的实例。
为了好玩,你可以试着像这样翻转一下:
order(UserID, RegionID, UserBalance, OrderID, ProductID, Price, ...) .
甚至
order(OrderID, UserID, RegionID, UserBalance, ProductID, Price, ...) .
并了解这些替代品对性能的影响。 (我可能错了; Eclipse是我最不了解的那个。)大多数Prolog只在没有其他信息的情况下对第一个参数进行索引。我不知道Eclipse是否有像SWI这样的索引声明,但在SWI的情况下,你曾经能够简单地说出这样的话:
:- index(order/7, [1,2]).
(假设7
是正确的arity)并且它将对前两个参数进行索引,这足以大大改善您的“挖掘”时间。现在这被忽略了a much more complex system is used instead这可能意味着你只需在SWI中尝试它就能看到性能优势。可能值得一看,因为你对此持开放态度。 Eclipse可能有类似的东西。
作为便携式选项,您可以使用term_hash/2
构建自己的索引。我自己从未使用过这个选项。我理解的基本思想是在单个术语中捆绑您可能查询的所有值,然后从该术语生成散列,并使用它来构建新关系,以便散列值是第一个参数。我怀疑这个选项看起来像这样(未经测试):
:- initialization rebuild_index/0.
:- dynamic order_by_order_id_and_user_id/2.
rebuild_index :-
order(OrderId, UserId, ...),
term_hash(order(OrderId, UserId), Hash),
assertz(order_by_order_id_and_user_id(Hash, order(OrderId, UserId, ...)).
find_order_by_order_id_and_user_id(OrderId, UserId, Order) :-
term_hash(order(OrderId, UserId), Hash),
order_by_order_id_and_user_id(Hash, Order).
这当然只有在Prolog要为动态谓词生成索引时才有效。
如果您使用的是SWI-Prolog,我也会(礼貌地)建议将数据库移动到RDBMS并使用ODBC接口进行查询。在数据库中优化性能要容易得多(我个人而不仅仅发布CREATE INDEX orders_by_order_id_and_user_id ON orders (order_id, user_id)
并且看到性能“神奇地”改进而不是像上面那样编写一堆样板访问代码)然后你得到的好处是RDBMS作为“集成技术”而不仅仅是持久性/存储技术。我不知道其他Prolog是否具有访问数据库的类似功能。
无论你发现什么有效,请回来提交作为答案,我想我们都会从了解更多关于各种替代方案的性能影响中获益。