Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

函数调用的成本

Posted on 2026-02-09

编程时,我们会将函数串联起来。函数 A 调用函数 B,依此类推。

你不必非得用这种方式编程,完全可以用一个函数编写整个程序。用一个函数编写一个非同寻常的程序会是一个很有趣的练习……前提是你把代码编写工作交给人工智能,因为人类很快就会被冗长的函数搞得焦头烂额。

编译器的一项关键优化是“内联”:编译器会获取你的函数定义,并尝试将其替换到调用位置。这在概念上非常简单。考虑以下示例,其中函数add3调用了函数add 。

 int add ( int x , int y ) { return x + y ; } int add3 ( int x , int y , int z ) { return add ( add ( x , y ), z ); }您可以按如下方式手动内联调用。 int add ( int x , int y ) { return x + y ; } int add3 ( int x , int y , int z ) { return add ( add ( x , y ), z ); }
 int add3 ( int x , int y , int z ) { return x + y + z ; }函数调用在性能上相对便宜,但并非完全免费。如果函数需要一些非平凡的参数,你可能需要将它们保存到栈上并进行恢复,因此会产生额外的加载和存储操作。你需要跳转到函数内部,并在函数结束后跳转出来。此外,根据你系统的函数调用约定以及你使用的指令类型,函数调用的开头和结尾可能还会有额外的指令。 int add3 ( int x , int y , int z ) { return x + y + z ; }

如果一个函数足够简单,例如我的add函数,那么在性能至关重要的情况下,它应该始终内联。让我们来看一个具体的例子。假设我要对一个数组中的整数求和。

 for ( int x : numbers ) { sum = add ( sum , x ); }我使用的是MacBook(M4处理器,LLVM虚拟机)。 for ( int x : numbers ) { sum = add ( sum , x ); }
功能 ns/int
常规的 0.7
排队 0.03

哇,内联版本速度快了20多倍。

让我们来看看发生了什么。’add’ 函数的调用点只是一个调用该函数的简单循环。

 ldr w1 , [ x19 ], #0 x4 bl 0x100021740 ; add(int, int) cmp x19 , x20 b.ne 0x100001368 ; <+28>该函数本身非常简单:仅需两条指令。 ldr w1 , [ x19 ], #0 x4 bl 0x100021740 ; add(int, int) cmp x19 , x20 b.ne 0x100001368 ; <+28>
 add w0 , w1 , w0 ret因此,每次加法运算需要6条指令。每次加法运算大约需要3个循环。 add w0 , w1 , w0 ret

内联函数呢?

 ldp q4 , q5 , [ x12 , #-0 x20 ] ldp q6 , q7 , [ x12 ], #0 x40 add.4s v0 , v4 , v0 add.4s v1 , v5 , v1 add.4s v2 , v6 , v2 add.4s v3 , v7 , v3 subs x13 , x13 , #0 x10 b.ne 0x1000013fc ; <+104>这完全不同。编译器已将加法运算转换为高级(SIMD)指令集,使用 8 条指令处理 16 个整数块。因此,每个整数只需半条指令(之前需要 6 条指令)。也就是说,指令数量减少了 12 倍。除了指令数量减少之外,处理器每个周期还能执行更多指令,从而大幅提升性能。 ldp q4 , q5 , [ x12 , #-0 x20 ] ldp q6 , q7 , [ x12 ], #0 x40 add.4s v0 , v4 , v0 add.4s v1 , v5 , v1 add.4s v2 , v6 , v2 add.4s v3 , v7 , v3 subs x13 , x13 , #0 x10 b.ne 0x1000013fc ; <+104>

如果我们阻止编译器使用这些高级指令,同时仍然进行内联,结果会怎样?我们仍然可以获得显著的性能提升(大约快 10 倍)。

功能 ns/int
常规的 0.7
排队 0.03
内联(无 SIMD) 0.07

好的。但是add函数有点极端了。我们知道它应该始终内联。那么,像计算字符串中空格数量的函数这样不那么简单的函数呢?

 size_t count_spaces ( std :: string_view sv ) { size_t count = 0 ; for ( char c : sv ) { if ( c == ' ' ) ++ count ; } return count ; }如果字符串足够长,那么函数调用的开销应该可以忽略不计。 size_t count_spaces ( std :: string_view sv ) { size_t count = 0 ; for ( char c : sv ) { if ( c == ' ' ) ++ count ; } return count ; }

让我们传入一个长度为 1000 个字符的字符串。

功能 ns/字符串
常规的 111
排队 115

内联版本不仅没有更快,反而略慢一些。我不确定原因。

如果我使用短字符串(比如 0 到 6 个字符)呢?那么内联函数的速度会明显更快。

功能 ns/字符串
常规的 1.6
排队 1.0

要点:

  1. 如果性能是首要考虑因素,应尽可能使用内联函数,这样可以显著提高性能。
  2. 对于速度可能快也可能慢的函数,是否内联取决于输入数据。对于字符串处理函数,字符串的大小可能决定是否需要内联才能获得最佳性能。

注: 我的源代码是公开的。

原文: https://lemire.me/blog/2026/02/08/the-cost-of-a-function-call/

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Alin Panaitiu
  • Anil Dash
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Big Technology
  • Bits about Money
  • Brandon Skerritt
  • Brent Simmons
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Christopher Butler
  • Colin Percival
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Dustin Curtis
  • eighty twenty
  • Elad Gil
  • Ellie Huxtable
  • Ethan Dalool
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • HeardThat Blog
  • Henrique Dias
  • Herman Martinus
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Li Haoyi
  • Liz Danzico
  • Lou Plummer
  • Luke Wroblewski
  • Maggie Appleton
  • Matt Baer
  • Matt Stoller
  • Matthias Endler
  • Mert Bulan
  • Mind Matters
  • Mostly metrics
  • Naval Ravikant
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • PostHog
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Rohit Patel
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Slava Akhmechet
  • Small Good Things
  • Steph Ango
  • Stephen Wolfram
  • Steve Blank
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • Understanding AI
  • Wes Kao
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2026 搞英语 → 看世界 | Design: Newspaperly WordPress Theme