草庐IT

performance - 在 Go 中,非捕获闭包会损害性能吗?

coder 2023-06-27 原文

例如,github.com/yhat/scrape建议使用这样的闭包:

func someFunc() {
    ...
    matcher := func(n *html.Node) bool {
        return n.DataAtom == atom.Body
    }
    body, ok := scrape.Find(root, matcher)
    ...
}

因为 matcher 实际上并不捕获任何局部变量,所以这可以等价地写成:

func someFunc() {
    ...
    body, ok := scrape.Find(root, matcher)
    ...
}

func matcher(n *html.Node) bool {
    return n.DataAtom == atom.Body
}

第一种形式看起来更好,因为匹配器函数非常特定于代码中的那个地方。但是它在运行时的表现是否更差(假设 someFunc 可能经常被调用)?

我想创建闭包一定有一些开销,但这种闭包可以被编译器优化为常规函数吗?

(显然语言规范不需要这个;我对 gc 的实际作用很感兴趣。)

最佳答案

好像没什么区别。我们可以 checkin 生成的机器码。

这是一个玩具程序:

package main

import "fmt"

func topLevelFunction(x int) int {
    return x + 4
}

func useFunction(fn func(int) int) {
    fmt.Println(fn(10))
}

func invoke() {
    innerFunction := func(x int) int {
        return x + 8
    }
    useFunction(topLevelFunction)
    useFunction(innerFunction)
}

func main() {
    invoke()
}

这是它的反汇编:

$ go version
go version go1.8.5 linux/amd64

$ go tool objdump -s 'main\.(invoke|topLevel)' bin/toy 
TEXT main.topLevelFunction(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
    toy.go:6    0x47b7a0    488b442408  MOVQ 0x8(SP), AX    
    toy.go:6    0x47b7a5    4883c004    ADDQ $0x4, AX       
    toy.go:6    0x47b7a9    4889442410  MOVQ AX, 0x10(SP)   
    toy.go:6    0x47b7ae    c3      RET         

TEXT main.invoke(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
    toy.go:13   0x47b870    64488b0c25f8ffffff  FS MOVQ FS:0xfffffff8, CX       
    toy.go:13   0x47b879    483b6110        CMPQ 0x10(CX), SP           
    toy.go:13   0x47b87d    7638            JBE 0x47b8b7                
    toy.go:13   0x47b87f    4883ec10        SUBQ $0x10, SP              
    toy.go:13   0x47b883    48896c2408      MOVQ BP, 0x8(SP)            
    toy.go:13   0x47b888    488d6c2408      LEAQ 0x8(SP), BP            
    toy.go:17   0x47b88d    488d052cfb0200      LEAQ 0x2fb2c(IP), AX            
    toy.go:17   0x47b894    48890424        MOVQ AX, 0(SP)              
    toy.go:17   0x47b898    e813ffffff      CALL main.useFunction(SB)       
    toy.go:14   0x47b89d    488d0514fb0200      LEAQ 0x2fb14(IP), AX            
    toy.go:18   0x47b8a4    48890424        MOVQ AX, 0(SP)              
    toy.go:18   0x47b8a8    e803ffffff      CALL main.useFunction(SB)       
    toy.go:19   0x47b8ad    488b6c2408      MOVQ 0x8(SP), BP            
    toy.go:19   0x47b8b2    4883c410        ADDQ $0x10, SP              
    toy.go:19   0x47b8b6    c3          RET                 
    toy.go:13   0x47b8b7    e874f7fcff      CALL runtime.morestack_noctxt(SB)   
    toy.go:13   0x47b8bc    ebb2            JMP main.invoke(SB)         

TEXT main.invoke.func1(SB) /home/vasiliy/cur/work/learn-go/src/my/toy/toy.go
    toy.go:15   0x47b8f0    488b442408  MOVQ 0x8(SP), AX    
    toy.go:15   0x47b8f5    4883c008    ADDQ $0x8, AX       
    toy.go:15   0x47b8f9    4889442410  MOVQ AX, 0x10(SP)   
    toy.go:15   0x47b8fe    c3      RET         

正如我们所见,至少在这个简单的例子中,topLevelFunctioninnerFunction (invoke.func1 ),并将它们传递给 useFunction,并被翻译成机器代码。

(将此与 innerFunction 捕获局部变量的情况进行比较是有益的;此外,innerFunction 通过全局变量传递的情况而不是函数参数——但这些留给读者作为练习。)

关于performance - 在 Go 中,非捕获闭包会损害性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45937924/

有关performance - 在 Go 中,非捕获闭包会损害性能吗?的更多相关文章

  1. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  2. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

    我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

  3. ruby - Ruby 中的闭包和 for 循环 - 2

    我是Ruby的新手,有些闭包逻辑让我感到困惑。考虑这段代码:array=[]foriin(1..5)array[5,5,5,5,5]这对我来说很有意义,因为i被绑定(bind)在循环之外,所以每次循环都会捕获相同的变量。使用每个block可以解决这个问题对我来说也很有意义:array=[](1..5).each{|i|array[1,2,3,4,5]...因为现在每次通过时都单独声明i。但现在我迷路了:为什么我不能通过引入一个中间变量来修复它?array=[]foriin1..5j=iarray[5,5,5,5,5]因为j每次循环都是新的,我认为每次循环都会捕获不同的变量。例如,这绝对

  4. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

    我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

  5. ruby-on-rails - Resque - 类的未定义方法 'perform' - 2

    我目前对后台队列不太满意。我正在尝试让Resque工作。我已经安装了redis和Resquegem。Redis正在运行。一个worker正在运行(rakeresque:workQUEUE=simple)。使用Web界面,我可以看到工作人员正在运行并等待工作。当我运行“rakeget_updates”时,作业已排队但失败了。我已经用defself.perform和defperform试过了。发条.raketask:get_updates=>:environmentdoResque.enqueue(GetUpdates)end类文件(app/workers/get_updates.rb)c

  6. ruby - 捕获 Ruby Logger 输出以进行测试 - 2

    我有一个像这样的ruby​​类:require'logger'classTdefdo_somethinglog=Logger.new(STDERR)log.info("Hereisaninfomessage")endend测试脚本行如下:#!/usr/bin/envrubygem"minitest"require'minitest/autorun'require_relative't'classTestMailProcessorClasses当我运行这个测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io一起玩得

  7. ruby - Capistrano 中的执行、测试和捕获命令有什么区别? - 2

    关于SSHkit-Github它说:Allbackendssupporttheexecute(*args),test(*args)&capture(*args)来自SSHkit-Rubydoc,我明白execute实际上是test的别名?test之间有什么区别?,execute,capture在Capistrano/SSHKit中我应该什么时候使用? 最佳答案 执行只是执行命令。使用非0退出引发错误。测试方法的行为与execute完全相同,但是它返回bool值(true如果命令以0退出,而false否则)。它通常用于控制任务中的流程

  8. ruby - 如何捕获 ruby​​ 中的所有异常? - 2

    我们如何捕获或/和处理ruby​​中所有未处理的异常?例如,这样做的动机可能是将某种异常记录到不同的文件或发送电子邮件给系统管理。在Java中我们会做Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandlerex);在Node.js中process.on('uncaughtException',function(error){/*code*/});在PHP中register_shutdown_function('errorHandler');functionerrorHandler(){$error=error_

  9. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  10. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

随机推荐