我不确定,如果一些奇怪的事情让我的代码更快:
使用内置操作或编写新的专用函数来做同样的事情通常会更好吗?(例如#'map仅用于向量的版本;我的版本通常在没有类型声明的情况下更快)
我应该定义新的(复杂的)类型以在声明中使用它们吗?(例如键入列表)
我应该直接为对象定义插槽吗?(例如px,py对于一个二维对象,或者可以使用一个pos向量类型的插槽,我可以将它重用于其他目的)
这有几个部分,但这里有一个快速的头脑风暴
使用内置分析器的 CL 发行版,例如我使用 sbcl http://www.sbcl.org/1.0/manual/Statistical-Profiler.html
sbcl 分析器的好处是一旦你分析了一个函数,如果你反汇编它,机器代码就会用统计信息进行注释。这需要对目标机器代码有一些了解。
不要低估您的实现:它们可以内置高级类型和流分析,并且能够,例如,在有意义时选择仅矢量版本的地图。
学习编译器宏:编译器宏可以隐藏函数,这让您可以根据表单的上下文进行额外的优化。IT 无需替换函数即可执行此操作,因此它仍然可以以更高阶的方式使用。
我发现这一系列的博客文章帮助我理解了这项技术http://nklein.com/tags/optimization/page/2/全部阅读!
一个重要的注意事项:永远不要就某种类型向编译器撒谎。类型声明是一种告诉编译器你知道类型是什么的方式,编译器甚至不必使用它们,当它使用时,它不必检查你是否给它正确的东西。
某些实现能够在某些条件下对某些数据类型进行拆箱。抱歉,这很模糊,但您需要仔细阅读您的实现。对于 sbcl,'sbcl internals' 指南非常有帮助。
例如:
(make-array 100 :element-type 'single-float :initial-element 0.0)
Run Code Online (Sandbox Code Playgroud)
可以存储为 sbcl 中的连续内存块。
我花了 3 个小时编写了一个基于 n 维矩阵乘法例程的疯狂编译器宏,然后针对 1 行内置解决方案对其进行了测试。对于 5 维以下的矩阵,差别不大!对于更高的维度,是的。它动摇了,但“性能优势”纯粹是学术性的,因为从未触及这些代码路径。幸运的是,我接受了这项任务是为了好玩,因为我问了你现在同样的问题。
世界上所有的类型说明符都不会给你 100 倍的性能提升。这来自更好的技术。阅读问题背后的数学原理,实现具有不同优势的不同辅助函数并在运行时在它们之间进行选择……然后返回并使用编译器宏来允许 lisp 在编译时进行选择。或者将技术指定为高阶函数,例如make-hash-table允许您指定散列函数和重新散列大小,这对于在某些大小下获得良好性能至关重要。
如果由于内存局部性问题而失去所有性能,算法复杂性就毫无意义。相反,如果通过在内核之间拆分问题,减少的数据集现在适合 l2 缓存,我们有时可以实现超线性性能特征。
BigO 是一个很好的指标,但这并不是故事的结局。这就是关联列表对于少量键和某些访问配置文件来说是哈希表的完全有效替代方案的原因。
我从 lisp 社区的某个地方听到了一个非常有效的金句:
Make it Fast and then make it Fast
如果没有别的,请遵循此。为自己吟唱吧!
让程序快速启动并运行,这样做你更有可能发现可以使用更好的技术或算法来获得几个数量级改进的地方。请首先使用CL自身的功能。不要过早地使用宏来交换 lisp 的高阶性质,探索使用函数可以走多远。
[编辑] 更多注释 - 以下是 sbcl
所以要直接回答你原来的问题: