Common lisp 编写快速公共Lisp代码
我不确定,如果一些奇怪的事情使我的代码更快: 使用内置操作或编写新的专用函数来做同样的事情通常更好吗? (例如,Common lisp 编写快速公共Lisp代码,common-lisp,Common Lisp,我不确定,如果一些奇怪的事情使我的代码更快: 使用内置操作或编写新的专用函数来做同样的事情通常更好吗? (例如,#map的一个版本仅用于向量;我的版本通常在没有类型声明的情况下更快) 我应该定义新的(复杂的)类型来在声明中使用它们吗? (例如,键入的列表) 我应该直接为对象定义插槽吗?(例如,二维对象的px和py,或者使用一个vector类型的插槽pos,我可以将其用于其他目的,这样可以吗)这有几个部分,但这里有一个快速的提示 轮廓 使用具有内置探查器的CL发行版,例如,我使用sbcl sbcl
#map
的一个版本仅用于向量;我的版本通常在没有类型声明的情况下更快)
我应该定义新的(复杂的)类型来在声明中使用它们吗?
(例如,键入的列表)
我应该直接为对象定义插槽吗?(例如,二维对象的
px
和py
,或者使用一个vector类型的插槽pos
,我可以将其用于其他目的,这样可以吗)这有几个部分,但这里有一个快速的提示
轮廓
使用具有内置探查器的CL发行版,例如,我使用sbcl
sbcl分析器的好处在于,一旦您分析了一个函数,如果您将其反汇编,机器代码就会用统计信息进行注释。这需要一些目标机器代码的知识
不要低估您的实现:它们可以内置高级类型和流分析,并且能够(例如)在有意义时选择仅矢量版本的map
了解编译器宏:编译器宏可以隐藏函数这为您提供了一个根据表单上下文进行额外优化的位置。它在不替换函数的情况下执行此操作,因此它仍然可以以更高的顺序使用
学习类型声明
我发现这一系列的博文帮助我理解了这一技巧——阅读em all
一个重要的注意事项是:永远不要在类型问题上对编译器撒谎。类型声明是告诉编译器您知道类型的一种方式,编译器甚至不必使用它们,当它使用时,它不必检查您是否给了它正确的东西
未绑定数据
某些实现能够在某些条件下取消绑定某些数据类型。抱歉,这是含糊不清的,但您需要仔细阅读您的实现。对于sbcl,“sbcl内部构件”指南非常有用
例如:
(make-array 100 :element-type 'single-float :initial-element 0.0)
可以存储为sbcl中的连续内存块
再次配置(使用真实数据)
我花了3个小时编写了一个基于疯狂编译器宏的n维矩阵乘法例程,然后用一行内置解决方案对它进行了测试。对于5维以下的婚姻来说,差别不大!对于更高的维度来说,是的,这是令人震惊的,但“性能优势”纯粹是学术性的,因为这些代码路径从未被触及。幸运的是,我承担这项任务是为了好玩,因为我问的问题与你现在的问题相同
算法
世界上所有的类型说明符都不会使性能提高100倍。这来自更好的技术。阅读问题背后的数学知识,实现具有不同强度的不同帮助函数,并在运行时进行选择……然后返回并使用编译器宏,以允许lisp在编译时进行选择。或者将该技术指定为高阶函数,例如,make hash table
允许您指定散列函数和重新散列大小,这对于在特定大小下获得良好性能至关重要
了解BigO的极限
如果由于内存局部性问题而失去了所有性能,那么算法的复杂性就毫无意义。相反,如果通过在核心之间分割问题,减少的数据集现在适合二级缓存,那么有时我们可以实现超线性性能特性
BigO是一个很好的指标,但它并不是故事的结尾。这就是为什么assoc列表是哈希表的一个完全有效的替代品,用于低数量的键和某些访问配置文件
总结
我从lisp社区的某个地方听到了一个非常有效的金咒:
快一点,然后快一点
如果没有其他的事情发生,那么接下来就是这个。自己唱吧
让程序快速启动并运行,这样做,您更有可能找到可以使用更好的技术或算法来实现几个数量级改进的地方。一定要先使用CL自己的功能。不要过早地使用宏来交换lisp的高阶特性,探索使用函数可以走多远
[编辑]更多注释-以下是sbcl的注释
- 结构插槽上的类型定义用于优化,类插槽的类型声明不用于优化
- 关于类型,从使程序易于编写和理解(使其快速)开始,然后查看访问时间是否是瓶颈(使其快速!)
- (插槽值x'名称)在名称已知时非常快。看看with slots是如何利用symbol macrolet的优势的
- 先内置(也可检查库)
- 它是否使问题更容易书写和理解
- 使用pos。当间接寻址的性能成为问题时,您将找到十几种其他方法来加速问题,解决方案将成为更广泛的优化技术的一部分