< 返回版块

bestgopher 发表于 2023-04-27 22:04

如代码所示,get_string函数内部声明了2个遍历,但是它们的地址不是相邻的,因为get_string把String返回了。而no_get_string内部的两个变量地址是相邻的。这有什么优化吗?

fn main() {
    let s1 = get_string();
    println!("in main s:{:p}", &s1);

    no_get_string();
}

#[inline(never)]
fn get_string() -> String {
    let a = 1;
    let s = "a".into();
    println!("in get_string a: {:p}", &a);
    println!("in get_string s: {:p}", &s);
    println!(
        "in get_string s - a: {}",
        &s as *const String as usize - &a as *const i32 as usize
    );
    s
}

#[inline(never)]
fn no_get_string() {
    let a = 1;
    let s = "a".into();
    println!("in no_get_string a: {:p}", &a);
    println!("in no_get_string s: {:p}", &s);
    println!(
        "in get_string s - a: {}",
        &s as *const String as usize - &a as *const i32 as usize
    );
}


运行结果:
in get_string a: 0x7ffee6a82a1c
in get_string s: 0x7ffee6a82b28
in get_string s - a: 268
in main s:0x7ffee6a82b28
in no_get_string a: 0x7ffee6a82a04
in no_get_string s: 0x7ffee6a82a08
in get_string s - a: 4

可以看到main中打印字符串的地址和get_string函数打印字符串地址相同,且函数中字符串和数字a的地址并不相邻。 而no_get_string中字符串地址和a的地址相邻。

评论区

写评论
作者 bestgopher 2023-04-28 09:42

主要是我又在看pin相关的内容了。以前通过函数返回一个自引用结构体,能模拟出问题,昨晚发现不能模拟了,一看原来返回的自引用结构体的地址不是在函数的栈中。

--
👇
guobbs: 原来也有老铁跟我一样,爱搞清楚底层的旯旯旮旮。

我最早开始是从VC++6.0学习C++,那个时候的学习理论返回的对象是函数栈中的一个副本,如果自定义对象很大,复制开销也很大,一般是不推荐这样设计接口获取返回值,经典的搞法是给接口函数传递对象指针,来获取返回结果。

但是这个理论到了C++11出现了颠覆,发现有很多接口又开始通过返回值来返回对象了,我对这个非常敏感,于是好奇做了大量实验,通过反汇编查看了一下代码实现,调用这样的接口都是在调用方的栈帧空间分配要接收结果的对象,接口内部能直接拿到调用方分配的对象地址,一下子明白了,上面的老兄提到 @viruscamp RVO(返回值优化)指的就是这个,网上有介绍说C++11中对比已经有了规定。这种方式对编程写代码非常友好。

刚接触到rust的时候,也是大量这样的写法(直接返回结果对象),每看一次都觉得不影响性能么(别扭)?后面也是通过反汇编查看,才释然了。

结合上面描述,回到楼主这个话题,就容易理解为什么 get_string()中s1的地址与 main 中 s 的地址是一样的了。

GUO 2023-04-28 09:33

原来也有老铁跟我一样,爱搞清楚底层的旯旯旮旮。

我最早开始是从VC++6.0学习C++,那个时候的学习理论返回的对象是函数栈中的一个副本,如果自定义对象很大,复制开销也很大,一般是不推荐这样设计接口获取返回值,经典的搞法是给接口函数传递对象指针,来获取返回结果。

但是这个理论到了C++11出现了颠覆,发现有很多接口又开始通过返回值来返回对象了,我对这个非常敏感,于是好奇做了大量实验,通过反汇编查看了一下代码实现,调用这样的接口都是在调用方的栈帧空间分配要接收结果的对象,接口内部能直接拿到调用方分配的对象地址,一下子明白了,上面的老兄提到 @viruscamp RVO(返回值优化)指的就是这个,网上有介绍说C++11中对比已经有了规定。这种方式对编程写代码非常友好。

刚接触到rust的时候,也是大量这样的写法(直接返回结果对象),每看一次都觉得不影响性能么(别扭)?后面也是通过反汇编查看,才释然了。

结合上面描述,回到楼主这个话题,就容易理解为什么 get_string()中s1的地址与 main 中 s 的地址是一样的了。

viruscamp 2023-04-27 23:59

返回值优化, RVO,相关文章 C++的比较多,但用在rust一样的。

smyone 2023-04-27 23:35

生命周期 应该是区分String生命周期

作者 bestgopher 2023-04-27 23:08

大佬,代码贴了

--
👇
viruscamp: 别直接贴 play.rust-lang.org 的地址,先 Share, 最好把代码直接贴一遍。

fn xxx(){
}
viruscamp 2023-04-27 22:37

别直接贴 play.rust-lang.org 的地址,先 Share, 最好把代码直接贴一遍。

fn xxx(){
}
1 共 6 条评论, 1 页