在Rust中使用本地函数是否有任何负面的性能影响?

cha*_*its 7 memory runtime function rust

我最近意识到我可以在Rust中创建本地函数(函数中的函数).似乎是一种清理代码而不会污染文件功能空间的好方法.我在下面通过局部函数与"外部"函数的小样本:

fn main() {
    fn local_plus(x: i64, y: i64) -> i64 {
        x + y
    }
    let x = 2i64;
    let y = 5i64;

    let local_res = local_plus(x, y);
    let external_res = external_plus(x,y);
    assert_eq!(local_res, external_res);
}

fn external_plus(x: i64, y: i64)  -> i64 {
    x + y
}
Run Code Online (Sandbox Code Playgroud)

我想知道这样做是否有任何负面的性能影响?就像Rust每次运行包含函数时重新声明函数或占用一些不需要的函数空间一样吗?或者它几乎没有性能影响?

作为一点点,任何有关我如何能够找到答案的提示(通过阅读任何特定的文档或我可以使用的工具)都会受到欢迎.

Mat*_* M. 6

作为一点点,任何有关我如何能够找到答案的提示(通过阅读任何特定的文档或我可以使用的工具)都会受到欢迎.

你知道Rust操场吗?

输入您的代码,单击"LLVM IR","Assembly"或"MIR"而不是"Run",您将看到为所述代码发出的低级表示.

我个人更喜欢LLVM IR(我习惯于从C++中读取它),这仍然比汇编更高,但仍然是语言.

我想知道这样做是否有任何负面的性能影响?

这是一个非常复杂的问题; 其实.

在Rust中本地或外部声明函数之间的唯一区别是作用域之一.在本地声明它只会缩小其范围.没有别的.

但是......范围和用法会对编译产生严重影响.

例如,仅使用一次的函数比使用10次的函数更可能被内联.编译器不能轻易估计pub函数的使用次数(无界),但对本地或非pub函数具有完美的知识.函数是否内联可能会严重影响性能配置文件(更糟或更好).

因此,通过缩小范围,从而限制使用,您鼓励编译器考虑您的内联函数(除非您将其标记为"冷").

另一方面,由于范围减小,因此无法共享(显然).

所以呢?

遵循用法:尽可能在最严格的范围内定义项目.

这是封装:现在,下次您需要修改此部分时,您将确切知道受影响的范围.

对Rust有一定的信任,如果可以避免它就不会引入开销.


lje*_*drz 5

没有影响; 我检查了为两个变体生成的程序集,它是相同的.

我比较的两个版本:

"外部":

fn main() {
    let x = 2i64;
    let y = 5i64;

    let external_res = external_plus(x,y);
}

fn external_plus(x: i64, y: i64)  -> i64 {
    x + y
}
Run Code Online (Sandbox Code Playgroud)

"本地":

fn main() {
    fn local_plus(x: i64, y: i64) -> i64 {
        x + y
    }
    let x = 2i64;
    let y = 5i64;

    let local_res = local_plus(x, y);
}
Run Code Online (Sandbox Code Playgroud)

两者都产生相同的asm结果(今天晚上的发布模式):

    .text
    .file   "rust_out.cgu-0.rs"
    .section    .text._ZN8rust_out4main17hb497928495d48c40E,"ax",@progbits
    .p2align    4, 0x90
    .type   _ZN8rust_out4main17hb497928495d48c40E,@function
_ZN8rust_out4main17hb497928495d48c40E:
    .cfi_startproc
    retq
.Lfunc_end0:
    .size   _ZN8rust_out4main17hb497928495d48c40E, .Lfunc_end0-_ZN8rust_out4main17hb497928495d48c40E
    .cfi_endproc

    .section    .text.main,"ax",@progbits
    .globl  main
    .p2align    4, 0x90
    .type   main,@function
main:
    .cfi_startproc
    movq    %rsi, %rax
    movq    %rdi, %rcx
    leaq    _ZN8rust_out4main17hb497928495d48c40E(%rip), %rdi
    movq    %rcx, %rsi
    movq    %rax, %rdx
    jmp _ZN3std2rt10lang_start17h14cbded5fe3cd915E@PLT
.Lfunc_end1:
    .size   main, .Lfunc_end1-main
    .cfi_endproc


    .section    ".note.GNU-stack","",@progbits
Run Code Online (Sandbox Code Playgroud)

这意味着生成的二进制文件中将存在零差异(不仅仅是性能方面).

更重要的是,如果你使用一个函数,它甚至不重要; 以下方法:

fn main() {
    let x = 2i64;
    let y = 5i64;

    let res = x + y;
}
Run Code Online (Sandbox Code Playgroud)

也产生相同的组件.

最重要的是,无论您是在内部main()还是外部声明函数,函数都会被内联.

编辑:正如Shepmaster指出的那样,在这个程序中没有副作用,因此两个变体生成的程序集实际上与以下内容相同:

fn main() {}
Run Code Online (Sandbox Code Playgroud)

但是,两者的MIR输出也是相同的(并且与空白的MIR输出不同main()),因此即使存在副作用,也不会有来自功能位置的任何差异.

  • 我很确定优化器正在删除程序中的所有内容,因为它没有可观察到的副作用.`res`是完全未使用的,所以我认为你会看到`fn main(){}`的相同内容. (2认同)