Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 消除;“神秘思考”;在这个公共Lisp函数中?_Performance_Common Lisp_Case - Fatal编程技术网

Performance 消除;“神秘思考”;在这个公共Lisp函数中?

Performance 消除;“神秘思考”;在这个公共Lisp函数中?,performance,common-lisp,case,Performance,Common Lisp,Case,这个常见的Lisp函数,它使用极其简单的幼儿园级算法和一些“案例”测试,简单地计算墙线框边的四个顶点,似乎负责为每个渲染帧动态分配196608字节;SBCL的分析器告诉我,这是我认为最有问题的函数。让我大致了解一下我在做什么,这是一个小的第一人称地牢爬虫游戏,一个地牢正好是32x32个牢房,每个牢房有4堵墙。32*32*4*x=196608,所以x变成48,正好是4*12(每面墙4*12个浮点数?可能不是) 现在,通过在游戏模式下使用OpenGL显示列表,我可以轻松缓解这个性能问题,我想这就是我

这个常见的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)))))
总结一下我试图解决的几个问题:

  • 在3处对“速度”进行“声明”以“优化”,在0处对其他所有内容进行“优化”(在该函数中以及唯一调用该函数的函数中)。奇怪的是,探查器确实报告了这个函数,考虑到稍微少一些。。。但它仍然在消耗。我的目标是零考虑。算术不应该是错误的

  • 然后我想“价值观”可能就是这么做的。也许,我认为,它在内部就像函数“list”一样,毫无疑问,它是conses(list函数在宇宙中的唯一用途)。我做了什么来缓解这种情况?为了实验起见,我修改了该文件以生成一个单壁顶点缓冲区全局数组,大小为适合12个float类型的元素,并修改了该函数以修改它,修改了调用函数以在调用该函数后从中读取(因此,它将不断更新保存在内存中同一位置的一组12个浮点数,而不是分配任何内容).奇怪的是,这并没有阻止这个函数成为一个深思熟虑的小猪!那么,…case在做深思熟虑吗?我确实发现有趣的是,前面提到的神秘数字是48.48=4*12,可能这4个case测试乘以每个'values'调用12个float。或者,这可能是巧合,48个字节意味着其他东西(因为浮点不是1字节,所以我怀疑是-是-其他东西)。这似乎很重要,但我不能完全理解我的下一个方法应该是什么

  • 尝试用“cond”等价物替换“case”,此时只是抓住稻草,也没有做任何事情

  • 那么,这个函数的“神秘考虑”从何而来呢?你们这些更有经验的Lisp程序员会如何处理这个棘手的问题呢


    (编辑)对于@FaheemMitha,是使用“计算墙点”函数的函数;该麻烦函数后来在计算墙点定义之前与(declaim(内联计算墙点))内联:

    (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)))))))))
    
    nil)

    消耗的内存是由分配浮点数引起的。每个函数调用返回浮点数,实际上是32位的
    单浮点数。考虑意味着在堆上分配了一些数据:cons单元格、数字、数组等

    单浮点
    是32位内存对象。4字节

    (+ 1.0 2.0)  ->  3.0
    
    在上述情况下,
    3.0
    是一个新的浮动,可能是新消耗的

    现在,除了上面的计算之外还有什么呢?内部
    +
    操作返回一个浮点
    3.0
    。它会发生什么情况

    • 它可以在处理器寄存器中返回,并用于下一个操作
    • 它可以在堆栈上返回,并在那里用于下一个操作
    • 在更复杂的操作中,它可能被分配到堆中,并作为指向堆值的指针返回。如果没有足够的寄存器用于所有返回值,或者堆栈帧的大小不足以用于所有返回值,则可能会出现这种情况
    现在,这些浮点数以后会发生什么?它们是否以某种方式存储在列表中?存储在新数组中?存储在新CLOS对象中的新结构中

    上面清楚地表明,这取决于处理器体系结构和编译器策略。x86没有多少寄存器。64位版本有更多寄存器。RISC处理器可能有更多寄存器。那么堆栈有多大,典型的堆栈帧有多大

    对于涉及多个函数的更复杂的计算,优化编译器可能能够优化哪些值保留在寄存器中,从而减少计算量

    上面也清楚地表明,对于普通的Lisp,没有完全通用的方法可以使浮点运算不考虑。减少考虑的能力取决于一些通用的想法和许多编译器/体系结构特定的技巧

    由于您使用的是SBCL,最好在SBCL邮件列表上征求建议,并告诉他们操作系统、体系结构(intel、arm等)以及它是以32位还是64位模式运行。还需要更多的上下文代码来更好地了解如何减少考虑

    一些阅读背景资料:

    • 肯·安德森(几年前不幸去世,一位非常有帮助的Lisper)收集了一些关于(存档版本)的信息

      • 编译器怎么说?如果你优化速度,它应该大声抱怨不能打开算术代码

        接下来,强制发生了什么?这也是开放编码的吗

        最后,请记住,您通常可以检查函数通过反汇编()生成的汇编代码。

        谢谢——我从来没有怀疑过浮点数是罪魁祸首(我抓到了足够多的鲱鱼来开一家海鲜餐厅)。现在我知道要找什么了,于是四处寻找
        (+ (+ 1.0 2.0) 4.0)  -> 7.0)