首页
文章
分类
系列
标签
关于
golang内联判断
发布于: 2023-1-14   更新于: 2023-1-14   收录于: golang
本文字数: 1148   阅读时长: 3 分钟   阅读量:

一、什么是函数内联

内联优化是什么? 内联(inlining)是编程语言编译器常用的优化手段,其优化的对象为函数,也称为函数内联。如果某函数F支持内联,则意味着编译器可以用F的函数体/函数定义替换掉对函数F进行调用的代码,以消除函数调用带来的额外开销,这个过程如下图所示: image.png 但是内联并不是只有优点而无缺点,因为内联,其实就是将一个函数调用原地展开,替换成这个函数的实现。当该函数被多次调用,就会被多次展开,这会增加编译后二进制文件的大小。而非内联函数,只需要保存一份函数体的代码,然后进行调用。所以,在空间上,一般来说使用内联函数会导致生成的可执行文件变大。

如何判断函数是否支持内联

我们先看下面的一段代码,testInline1、testInline2、testInline3是否能够被内联,我们猜想的是testInline1能够被内联,testInline2和testInline3不能够被内联。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
	"fmt"
)

func main() {
	fmt.Println(testInline1(1, 2))
	fmt.Println(testInline2(1, 2))
	fmt.Println(testInline3(1, 2))
}

func testInline1(a, b int) int {
	if a > b {
		return a
	}
	return b
}

//go:noinline
func testInline2(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func testInline3(a, b int) int {
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
	if a > b {
		return a
	}
	return b
}

方法1:使用go tool compile

我们可以使用go tool compile查看函数是否能够被内联:

1
go tool compile -m=2 test.go
  • testInline1符合我们预期,是不能被内联的
  • testInline2由于被打上注释“//go:noinline”,所以也不会被内联
  • testInline3则是因为函数过于复杂,超过阈值80了,所以也不能被内联
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ go tool compile -m=2 test.go
test.go:15:6: can inline testInline1 with cost 8 as: func(int, int) int { if a > b { return a }; return b }
test.go:23:6: cannot inline testInline2: marked go:noinline
test.go:30:6: cannot inline testInline3: function too complex: cost 96 exceeds budget 80
test.go:32:14: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
test.go:9:6: cannot inline main: function too complex: cost 359 exceeds budget 80
test.go:10:25: inlining call to testInline1 func(int, int) int { if a > b { return a }; return b }
test.go:10:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
test.go:11:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
test.go:12:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }

方法2:使用go build

由于go tool compile仅支持单个文件,使用go build编译一个目录查询函数是否被内联 执行如下命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ go build -gcflags="-m=2" .
.\test.go:15:6: can inline testInline1 with cost 8 as: func(int, int) int { if a > b { return a }; return b }
.\test.go:23:6: cannot inline testInline2: marked go:noinline
.\test.go:30:6: cannot inline testInline3: function too complex: cost 96 exceeds budget 80
.\test.go:32:14: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
.\test.go:9:6: cannot inline main: function too complex: cost 359 exceeds budget 80
.\test.go:10:25: inlining call to testInline1 func(int, int) int { if a > b { return a }; return b }
.\test.go:10:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
.\test.go:11:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }
.\test.go:12:13: inlining call to fmt.Println func(...interface {}) (int, error) { var fmt..autotmp_3 int; fmt..autotmp_3 = <nil>; var fmt..autotmp_4 error; fmt..autotmp_4 = <nil>; fmt..autotmp_3, fmt..autotmp_4 = fmt.Fprintln(io.Writer(os.Stdout), fmt.a...); return fmt..autotmp_3, fmt..autotmp_4 }

Written with StackEdit.

赞赏
微信
取消