编写快速Common Lisp代码

时间:2015-03-02 22:53:51

标签: common-lisp

我不确定,如果一些奇怪的事情使我的代码更快:

使用内置操作或编写新的专用函数通常会更好吗? (例如,仅适用于向量的#'map版本;我的版本通常在没有类型声明的情况下更快)

我应该定义新的(复杂的)类型以在声明中使用它们吗? (例如打字列表)

我应该直接为对象定义插槽吗? (例如,对于二维对象,pxpy,或者可以使用类型向量的一个插槽pos,我可以将其重用于其他目的)

1 个答案:

答案 0 :(得分:4)

这有几个部分,但这是一个快速的题库

PROFILE!

使用内置了探查器的CL发行版,我使用sbcl作为示例http://www.sbcl.org/1.0/manual/Statistical-Profiler.html

关于sbcl分析器的好处是,一旦你对一个函数进行了分析,如果你对它进行反汇编,机器代码就会用统计数据进行注释。这需要一些目标机器代码的知识。

不要低估您的实施:它们可以内置高级类型和流量分析,并且能够在有意义的情况下选择仅矢量版本的地图。

学习编译器宏:编译器宏可以使用阴影函数,这使您可以根据表单的上下文进行额外的优化。 IT部门在不更换功能的情况下执行此操作,因此仍可以按更高顺序使用。

学习类型声明

我发现这一系列博客文章帮助我理解了这项技巧http://nklein.com/tags/optimization/page/2/全部阅读!

ONE MASSIVE注意:不要对编译器撒谎。类型声明是告诉编译器你知道编译器甚至不必使用它们的类型的一种方式,当它发生时它不必检查你给它正确的东西。

未装箱数据

某些实现能够在某些条件下取消装入某些数据类型。抱歉这是模糊的,但您需要阅读您的实施。对于sbcl来说,'sbcl internals'指南非常有帮助。

例如:

(make-array 100 :element-type 'single-float :initial-element 0.0)

可以在sbcl中存储为连续的内存块。

再次查询(使用实际数据)

我花了3个小时编写一个基于n维矩阵乘法例程的疯狂编译器宏,然后针对内置解决方案的1行进行测试。对于5维以下的matricies,没有太大的区别!对于更高的维度,是的它震撼但“性能优势”纯粹是学术性的,因为那些代码路径从未被触及过。幸运的是,当我问你现在的同样问题时,我承担了这项任务的乐趣。

算法

世界上所有类型说明符都不会给你100倍的性能提升。这来自更好的技术。阅读问题背后的数学,实现具有不同强度的不同辅助函数,并在运行时在它们之间进行选择...然后返回并使用编译器宏来允许lisp在编译时选择。或者将该技术指定为更高阶函数,例如make-hash-table允许您指定散列函数和重新散列大小,这对于在特定大小下获得良好性能至关重要。

了解BigO的限制

如果由于内存局部性问题而导致所有性能下降,算法复杂性就没有任何意义。相反,如果通过在核心之间拆分问题,减少的数据集现在适合l2缓存,我们可以实现超线性性能特征。

BigO是一个很好的指标,但它不是故事的结尾。这就是为什么关联列表是用于少量密钥和某些访问配置文件的散列表的完全有效替代的原因。

摘要

我在lisp社区的某个地方听到了一个很好的口头禅:#/ p>

Make it Fast and then make it Fast

如果没有别的话。吟唱给自己!

让程序快速启动并运行,这样你就更有可能发现可以使用更好的技术或算法来获得数个数量级改进的地方。 首先使用CL自己的功能。不要过早地使用宏来交易lisp的高阶性质,探索你可以在多大程度上使用函数。

[编辑]更多备注 - 以下内容适用于sbcl

  • struct slot上的类型定义用于优化,类槽的类型声明不是。
  • 关于类型,从什么使程序易于编写和理解(快速启动)开始,然后查看访问时间(如果它是瓶颈)(快速制作!)
  • 名称已知时,
  • (slot-value x'name)非常快。看看with-slots如何使用symbol-macrolet来实现它的优势

所以直接回答你原来的问题:

  • 首先内置(也检查库)
  • 是否使问题更易于编写和理解?
  • 使用pos。当该间接的性能成为问题时,您将找到其他方法来加速解决问题,解决方案将成为更广泛的优化技术的一部分。