Optimization 如何使SBCL优化FDEFINITION的可能调用?

Optimization 如何使SBCL优化FDEFINITION的可能调用?,optimization,lisp,common-lisp,sbcl,Optimization,Lisp,Common Lisp,Sbcl,抱歉:我没有足够的知识将其作为一个易于理解的代码片段进行修改 我一直在使用SBCL编译器注释作为可能改进的标志,但我对这一点完全不了解- ; compiling (DEFUN EXECUTE-PARALLEL ...) ; file: /home/dunham/8000-benchmarksgame/bench/spectralnorm/spectralnorm.sbcl-8.sbcl ; in: DEFUN EXECUTE-PARALLEL ; (FUNCALL FUNCTION ST

抱歉:我没有足够的知识将其作为一个易于理解的代码片段进行修改

我一直在使用SBCL编译器注释作为可能改进的标志,但我对这一点完全不了解-

; compiling (DEFUN EXECUTE-PARALLEL ...)
; file: /home/dunham/8000-benchmarksgame/bench/spectralnorm/spectralnorm.sbcl-8.sbcl
; in: DEFUN EXECUTE-PARALLEL
;     (FUNCALL FUNCTION START END)
; --> SB-C::%FUNCALL THE 
; ==>
;   (SB-KERNEL:%COERCE-CALLABLE-FOR-CALL FUNCTION)
; 
; note: unable to
;   optimize away possible call to FDEFINITION at runtime
; because:
;   FUNCTION is not known to be a function
-

这个节目很有趣。类似程序的测量结果如下所示


让SBCL优化掉对FDEFINITION的可能调用是可行的,还是编译器注意到的是解释而不是机会?

可能调用FDEFINITION的原因是它不知道函数是一个函数:它可能是一个函数的名称:通常它可能是一个函数指示符,而不是一个函数。为了使编译器保持安静,向它解释它是一个具有适当类型声明的函数,即declare type function function:您只需要声明它的类型是function

Rainer是对的:考虑到您正在启动一个新线程,这有可能成为一个性能问题。特别是,添加声明很可能不会产生任何影响:

如果没有声明,对funcall的调用将被编译为类似“检查对象的类型:如果它是函数,则调用它;如果不是,则对其调用fdefinition并调用结果;'; 通过声明,整个函数看起来像‘检查对象是函数,如果不是,则发出错误信号。。。调用函数“”。 在这两种情况下,如果对象是函数,则有一个类型检查和一个调用:类型检查只是在不同的位置。在第一种情况下,如果对象仅仅是函数的名称,那么代码仍然有效,而使用类型检查则不会


在这两种情况下,这都是你关心调用makethread的代码:如果这和函数调用一样快,即使是通过fdefinition,我也会对线程系统印象深刻!几乎可以肯定,此函数的性能完全取决于生成线程的开销。

可能调用fdefinition的原因是它不知道函数是一个函数:它可能是一个函数的名称:一般来说,它可能是一个函数指示符,而不是一个函数。为了使编译器保持安静,向它解释它是一个具有适当类型声明的函数,即declare type function function:您只需要声明它的类型是function

Rainer是对的:考虑到您正在启动一个新线程,这有可能成为一个性能问题。特别是,添加声明很可能不会产生任何影响:

如果没有声明,对funcall的调用将被编译为类似“检查对象的类型:如果它是函数,则调用它;如果不是,则对其调用fdefinition并调用结果;'; 通过声明,整个函数看起来像‘检查对象是函数,如果不是,则发出错误信号。。。调用函数“”。 在这两种情况下,如果对象是函数,则有一个类型检查和一个调用:类型检查只是在不同的位置。在第一种情况下,如果对象仅仅是函数的名称,那么代码仍然有效,而使用类型检查则不会

在这两种情况下,这都是你关心调用makethread的代码:如果这和函数调用一样快,即使是通过fdefinition,我也会对线程系统印象深刻!几乎可以肯定,此函数的性能完全取决于生成线程的开销。

在实际代码中,除非确实需要,否则请避免这样的优化

让SBCL优化掉对FDDEFINITION的可能调用是否切实可行,或者编译器注意到的是一个解释而不是一个机会

一般来说,这并不重要,尤其是因为大多数Lisp代码不应使用优化质量speed 3 safety 0 space 0进行编译,因为它可能会导致软件出现运行时错误和崩溃,具体取决于所使用的实现和程序。在没有安全性的情况下,通过funcall调用除函数或符号命名函数以外的未选中对象可能会非常危险,导致程序崩溃

对于特定的基准测试,可以通过计时来检查类型声明和专门的fdefinition编译是否有任何好处

类型声明

用于明确名为fn的变量引用function类型的对象的类型声明如下:

(declare (type function fn))
在特定的基准测试程序中,无论如何都不会调用FDEFINITION

在您提供的示例中,无论如何都不会调用fdefinition

(setf foo (lambda (x) x))       ; foo references a function object

(funcall foo 3)
funcall可能是通过以下方式实现的:

(etypecase f
  ((or cons symbol) (funcall (fdefinition f) ...))
  (function         ...))
由于代码传递函数对象,因此永远不需要调用fdefinition

优化的好处是,运行时类型分派可以被删除,cons或symbol情况下的死代码…

在实际代码中,避免操作 像那样的胆怯——除非真的需要

让SBCL优化掉对FDDEFINITION的可能调用是否切实可行,或者编译器注意到的是一个解释而不是一个机会

一般来说,这并不重要,尤其是因为大多数Lisp代码不应使用优化质量speed 3 safety 0 space 0进行编译,因为它可能会导致软件出现运行时错误和崩溃,具体取决于所使用的实现和程序。在没有安全性的情况下,通过funcall调用除函数或符号命名函数以外的未选中对象可能会非常危险,导致程序崩溃

对于特定的基准测试,可以通过计时来检查类型声明和专门的fdefinition编译是否有任何好处

类型声明

用于明确名为fn的变量引用function类型的对象的类型声明如下:

(declare (type function fn))
在特定的基准测试程序中,无论如何都不会调用FDEFINITION

在您提供的示例中,无论如何都不会调用fdefinition

(setf foo (lambda (x) x))       ; foo references a function object

(funcall foo 3)
funcall可能是通过以下方式实现的:

(etypecase f
  ((or cons symbol) (funcall (fdefinition f) ...))
  (function         ...))
由于代码传递函数对象,因此永远不需要调用fdefinition


优化的好处是,运行时类型分派可以删除,cons或symbol情况下的死代码…

您提出了一个关于删除fdefinition的问题,但实际上您的问题依赖于一个前提,即sbcl注释是推动优化和改进的好方法。注释是发现明显问题和类型声明有帮助的地方的好方法。他们不会告诉你是什么让你的程序变慢。提高程序性能的正确方法是1。思考是否有更快的算法,以及2。衡量它的性能,找出慢的地方

一个fdefinition调用只有在紧密循环中发生时才起作用,即它不是单个的,而是非常复数的

在这种情况下,它恰好启动了一个线程。如果在紧循环中启动线程,那么性能问题来自于在紧循环中启动线程。不要那样做


如果您没有在一个紧密的循环中启动线程,查看您的代码,那么看起来您没有,还有更重要的事情要做。当您可以优化内部函数时,为什么要浪费时间在FD定义上?每次调用可能会被调用4次以并行执行。

您提出了一个关于删除FD定义的问题,但实际上您的问题依赖于一个前提,即sbcl注释是推动优化和改进的一个好方法。注释是发现明显问题和类型声明有帮助的地方的好方法。他们不会告诉你是什么让你的程序变慢。提高程序性能的正确方法是1。思考是否有更快的算法,以及2。衡量它的性能,找出慢的地方

一个fdefinition调用只有在紧密循环中发生时才起作用,即它不是单个的,而是非常复数的

在这种情况下,它恰好启动了一个线程。如果在紧循环中启动线程,那么性能问题来自于在紧循环中启动线程。不要那样做


如果您没有在一个紧密的循环中启动线程,查看您的代码,那么看起来您没有,还有更重要的事情要做。当你可以优化内部函数时,为什么要浪费时间在FD定义上,每次调用可能会被调用4次以并行执行。

这在你的程序中重要吗?比其他Lisp程序慢几百倍,这足够重要吗?这是你需要决定的。此外,还需要确保使用正确的数据类型调用FUNCALL。现在,在安全代码中,FUNCALL应该接受三件事:1函数对象,2个表示函数的符号,3在任何其他情况下它都会发出错误信号。如果您只想传递函数对象,那么您必须确保可能不需要and then 2和3。您似乎不希望为这种特定情况提供特定的答案。这在您的程序中重要吗?比其他Lisp程序慢几百倍,这是否足够重要?这是您需要决定的。此外,还需要确保使用正确的数据类型调用FUNCALL。现在,在安全代码中,FUNCALL应该接受三件事:1函数对象,2个表示函数的符号,3在任何其他情况下它都会发出错误信号。如果只想传递函数对象,然后你必须确保可能不需要,然后2和3。你似乎不希望为这种特定情况提供特定的答案。>>一个合适的类型声明declaration type函数…你似乎不希望为这种特定情况提供特定的答案。嗯:我告诉过你要使用什么声明表单,您还需要什么?谢谢,您修改后的答案正是我所需要的。>>一个合适的类型声明declare type函数…您似乎不希望提供一个说明符
我告诉过你要用什么申报表,你还想要什么?谢谢你,你修改后的答案就是我需要的。不,我的问题不依赖于你陈述的前提。我不认为这是一个好方法——这是我的方法。不,我的问题不依赖于你陈述的前提。我不认为这是一个好方法——这是我的方法。正如丹·罗伯逊可能猜到的,我用speed 3编译,因为这似乎迫使我打印编译器注释,我可以搜索提示。事实上,我一直在寻找机会删除2010年其他人添加到源文件顶部的声明优化速度3安全0空间0。大约一周前,在HN上,有人嘲笑此人的Lisp程序做了一些愚蠢的危险事情,你可能永远不应该在真正的软件上打开,因为它们可能会让它表现得非常糟糕,而不仅仅是在你有bug的时候崩溃。那个旧程序是为旧SBCL和32位操作系统编写的。现在显示了性能相当的程序,这些程序没有将安全设置为0,也没有打印十几条编译器注释。@igouy:也可以忽略编译器注释,或者告诉编译器闭嘴……正如Dan Robertson可能猜到的那样,我用speed 3编译,因为这似乎迫使我打印编译器注释,我可以搜索提示。事实上,我一直在寻找机会删除2010年其他人添加到源文件顶部的声明优化速度3安全0空间0。大约一周前,在HN上,有人嘲笑此人的Lisp程序做了一些愚蠢的危险事情,你可能永远不应该在真正的软件上打开,因为它们可能会让它表现得非常糟糕,而不仅仅是在你有bug的时候崩溃。那个旧程序是为旧SBCL和32位操作系统编写的。现在显示的是性能相当的程序,这些程序没有将安全设置为0,也没有打印十几条编译器注释。@igouy:也可以忽略编译器注释,或者让编译器闭嘴。。。