7 performance case common-lisp
这个Common Lisp函数简单地计算了墙的线框边缘的四个顶点,非常简单的幼儿园级算术和一些"大小写"测试似乎负责为每个渲染帧动态分配196608个字节; SBCL的分析器告诉我,就业务而言,这是我最有问题的功能.为了大致了解我正在做什么,它是一个小型的第一人称地下城爬虫游戏,地牢正好是32x32个细胞,每个细胞有4个墙.32*32*4*x = 196608,所以x结果是48,恰好是4*12(每面墙4个墙*12个浮点数?也许不是).
现在,我可以通过在游戏模式下使用OpenGL显示列表轻松缓解这个性能问题,我想这就是我将要做的事情.尽管如此,1)我通常不会过早地进行优化,更重要的是2)我仍然不喜欢留下像这样没有划痕的麻烦痒,我想知道我还能做些什么.我的功能如下,如下:
(defun calculate-wall-points (x y wall)
(declare (integer x y)
(keyword wall))
"Return the 4 vertices (12 floats) of a given dungeon cell wall"
(let ((xf (coerce x 'float))
(yf (coerce y 'float)))
(case wall
(:SOUTH
(values xf yf 0.0
(1+ xf) yf 0.0
(1+ xf) yf 1.0
xf yf 1.0))
(:WEST
(values xf yf 0.0
xf yf 1.0
xf (1+ yf) 1.0
xf (1+ yf) 0.0))
(:NORTH
(values xf (1+ yf) 0.0
xf (1+ yf) 1.0
(1+ xf) (1+ yf) 1.0
(1+ xf) (1+ yf) 0.0))
(:EAST
(values (1+ xf) (1+ yf) 0.0
(1+ xf) (1+ yf) 1.0
(1+ xf) yf 1.0
(1+ xf) yf 0.0))
(otherwise
(error "Not a valid heading passed for wall in function calculate-wall-points: ~A" wall)))))
Run Code Online (Sandbox Code Playgroud)
总结一下我试图解决的一些事情:
在3处对'speed'进行'declare'以'优化',在0处对其他所有进行'优化'(在此函数中,以及调用它的唯一函数).奇怪的是,剖析器确实报告了这个功能稍微减少......但它仍然存在.我的目标是零消费.算术不应该缺点.
然后我认为'价值'可能会这样做.也许,我认为,它内部就像功能'列表',毫无疑问,这是一个('列表'功能在宇宙中的唯一目的).我做了什么来试图缓解这个?仅仅是为了实验,我修改了文件以制作一个wall-vertex-buffer全局数组,大小适合float类型的12个元素,并修改了这个函数来修改它,并调用它后调用它的调用函数函数(因此它会不断更新一组12个浮点数保存在内存中的相同位置,而不是分配任何东西).奇怪的是,这并没有阻止这个功能成为一个小猪!所以......做案的'案例'?我觉得有趣的是,前面提到过,神秘数字是48. 48 = 4*12,也许这4个案例测试每次'值'调用12次浮点数.或者,这可能是巧合,48字节意味着其他东西(因为浮点数不是1字节,我怀疑是 - 是 - 其他的东西).这看起来很重要,但我不能完全理解我的下一步应该是什么.
尝试用'cond'替代'替换'case',此时只是抓住吸管,也没有做任何事情.
那么这个功能的"神秘消息"来自哪里呢?你如何更有经验的Lisp程序员接近这个棘手的问题?
(编辑)@FaheemMitha,是使用calculate-wall-points函数的函数; 这个麻烦的函数后来在calculate-wall-points的定义之前用(declaim(inline calculate-wall-points))内联:
(defun render-dungeon-room (dungeon-object x y)
(declare (optimize (speed 3) (space 0) (debug 0)))
(declare (type fixnum x y))
(let ((cell (cell-at dungeon-object x y)))
(unless (null cell)
(dolist (wall-heading +basic-headings+)
(unless (eq wall-heading (opposite-heading *active-player-heading*))
(when (eql (get-wall-type cell wall-heading) :NORMAL)
(multiple-value-bind (v1x v1y v1z v2x v2y v2z v3x v3y v3z v4x v4y v4z)
(calculate-wall-points x y wall-heading)
(declare (type float v1x v1y v1z v2x v2y v2z v3x v3y v3z v4x v4y v4z))
(gl:with-primitive :quads
(if (is-edit-mode)
(case wall-heading
(:NORTH
(gl:color 0.4 0.4 0.4))
(:WEST
(gl:color 0.4 0.0 0.0))
(:SOUTH
(gl:color 0.0 0.0 0.4))
(:EAST
(gl:color 0.0 0.4 0.0)))
(gl:color 0.1 0.1 0.1))
(gl:vertex (the float v1x)
(the float v1y)
(the float v1z))
(gl:vertex (the float v2x)
(the float v2y)
(the float v2z))
(gl:vertex (the float v3x)
(the float v3y)
(the float v3z))
(gl:vertex (the float v4x)
(the float v4y)
(the float v4z)))
(gl:color 1.0 1.0 1.0)
(gl:with-primitive :line-loop
(gl:vertex (the float v1x)
(the float v1y)
(the float v1z))
(gl:vertex (the float v2x)
(the float v2y)
(the float v2z))
(gl:vertex (the float v3x)
(the float v3y)
(the float v3z))
(gl:vertex (the float v4x)
(the float v4y)
(the float v4z)))))))))
Run Code Online (Sandbox Code Playgroud)
零)
该consed内存通过分配花车引起的.每个函数调用返回浮点数,实际上是32位single-floats.Consing意味着在堆上分配一些数据:cons细胞,数字,数组,......
A single-float是32位内存对象.4字节.
(+ 1.0 2.0) -> 3.0
Run Code Online (Sandbox Code Playgroud)
在上面的情况下3.0是一个新的浮动,可能是新的consed.
(+ (+ 1.0 2.0) 4.0) -> 7.0)
Run Code Online (Sandbox Code Playgroud)
现在上面的计算是什么?内部+操作返回一个浮点数3.0.怎么了?
现在这些花车会发生什么?他们以某种方式存储?在列表中?在一个新阵列?在一个新的structure?在一个新的CLOS对象?
上面说明它取决于处理器架构和编译器策略.x86的寄存器不多.64位版本有更多.RISC处理器可能有更多的寄存器.那么堆栈有多大,典型的堆栈帧有多大?
对于涉及多个函数的更复杂的计算,优化编译器可能能够优化哪些值保留在寄存器中,从而减少消耗.
上面还清楚地表明,对于Common Lisp,没有完全通用的配方如何使浮动操作不存在.减少使用的能力取决于一些一般的想法和许多编译器/体系结构特定的技巧.
由于您使用SBCL,最好是索要SBCL邮件列表上的建议,并告诉他们有关操作系统,架构(英特尔,ARM,...),如果它在32位或64位模式下运行.还需要更多的上下文代码来更好地了解如何减少消耗.
一些阅读背景信息:
| 归档时间: |
|
| 查看次数: |
507 次 |
| 最近记录: |