< 返回版块

发表于 2021-07-31 16:50

Tags:future,memeory,内存

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bf2d79be35661ce80cbdf2be405e258f

每加一层调用,就加128个字节


Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bf2d79be35661ce80cbdf2be405e258f

评论区

写评论
作者 2021-08-09 13:11

好的

--
👇
catsalwaysmeow: 抱歉我不能加陌生人好友哦。我也在社区二群,有问题可以在群里讨论呀。

catsalwaysmeow 2021-08-06 13:59

抱歉我不能加陌生人好友哦。我也在社区二群,有问题可以在群里讨论呀。

--
👇
宁: 加个q 《393零93零》 一起学习探讨

catsalwaysmeow 2021-08-06 13:50

嗯嗯,弄清楚就好了。没想到想将值直接放到堆上仍然依赖release的优化,这个例子构造得真棒。

--
👇
宁: 明白,看看我的测试,确实像你说的,即使用 Box::pin,变量在stack中也会爆 https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=3f447dc77d000564c11072131c0dca1c

作者 2021-08-05 11:31

明白,看看我的测试,确实像你说的,即使用 Box::pin,变量在stack中也会爆 https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=3f447dc77d000564c11072131c0dca1c

--
👇
catsalwaysmeow: 找到原因了。我之前的想法有一点问题,注意不要被误导了!因为Drop的存在,局部变量都会被放到状态机中。如果不关心某一个变量的Drop,可以通过添加大括号改变它的作用域,这样子它就有机会被排除到状态机之外了。

参考:https://tmandry.gitlab.io/blog/posts/optimizing-await-2/#liveness-and-drop

--
👇
catsalwaysmeow: 我也测试了一下。如果没有用Box::pin包起来,SoBigAField的确在调用栈中(栈顶地址与栈底地址之间)。作用域没有跨越.await的变量,的确没有被保存在状态机中,也就没有增加impl Future的大小。但「作用域」意味着必须用大括号把它包含起来,否则,即使对它的使用没有跨越.await,它也会被包括到状态机中。有可能是因为编译器还不够聪明,也可能是因为存在其它我没有注意到的考量。

catsalwaysmeow 2021-08-04 21:07

找到原因了。我之前的想法有一点问题,注意不要被误导了!因为Drop的存在,局部变量都会被放到状态机中。如果不关心某一个变量的Drop,可以通过添加大括号改变它的作用域,这样子它就有机会被排除到状态机之外了。

参考:https://tmandry.gitlab.io/blog/posts/optimizing-await-2/#liveness-and-drop

--
👇
catsalwaysmeow: 我也测试了一下。如果没有用Box::pin包起来,SoBigAField的确在调用栈中(栈顶地址与栈底地址之间)。作用域没有跨越.await的变量,的确没有被保存在状态机中,也就没有增加impl Future的大小。但「作用域」意味着必须用大括号把它包含起来,否则,即使对它的使用没有跨越.await,它也会被包括到状态机中。有可能是因为编译器还不够聪明,也可能是因为存在其它我没有注意到的考量。

catsalwaysmeow 2021-08-04 20:54

我也测试了一下。如果没有用Box::pin包起来,SoBigAField的确在调用栈中(栈顶地址与栈底地址之间)。作用域没有跨越.await的变量,的确没有被保存在状态机中,也就没有增加impl Future的大小。但「作用域」意味着必须用大括号把它包含起来,否则,即使对它的使用没有跨越.await,它也会被包括到状态机中。有可能是因为编译器还不够聪明,也可能是因为存在其它我没有注意到的考量。

--
👇
宁: 理解了,我做个实验看是不是这样

--
👇
catsalwaysmeow: 我也在学习async/await,还有许多东西没有弄明白,下面的理解可能有错哦。

编译器为async函数生成的匿名结构(返回的impl Future)储存了跨越了.await的变量(SoBigAField),以及必要的标志。原则上,它只是一个普通的结构,具体被放置在哪里取决于「调用者」如何处理它:

如果「调用者」用Box::pin把它包了起来,那么它就在Allocator分配的内存中(通常是堆)。

如果「调用者」只是将它赋值到了一个局部变量local_future中,分为两种情况。 如果local_future在「调用者」的代码中被.await了,那么local_future会被放置在「调用者」返回的匿名结构中,它被放置在哪里又取决于「调用者」的「调用者」... 如果local_future没有被.await,那么它会被放置到「调用者」的调用栈中。

作者 2021-08-04 18:05

理解了,我做个实验看是不是这样

--
👇
catsalwaysmeow: 我也在学习async/await,还有许多东西没有弄明白,下面的理解可能有错哦。

编译器为async函数生成的匿名结构(返回的impl Future)储存了跨越了.await的变量(SoBigAField),以及必要的标志。原则上,它只是一个普通的结构,具体被放置在哪里取决于「调用者」如何处理它:

  • 如果「调用者」用Box::pin把它包了起来,那么它就在Allocator分配的内存中(通常是堆)。

  • 如果「调用者」只是将它赋值到了一个局部变量local_future中,分为两种情况。

    • 如果local_future在「调用者」的代码中被.await了,那么local_future会被放置在「调用者」返回的匿名结构中,它被放置在哪里又取决于「调用者」的「调用者」...
    • 如果local_future没有被.await,那么它会被放置到「调用者」的调用栈中。

--
👇
宁: 没注意,box::pin是一个方案,不用关Sleep的align,多谢多谢! 另外,我一直有一个疑问,future在运行的时候 async_fn_aligned_structure 里的 SoBigAField 是放在栈上的,还是在堆上的。

--
👇
宁: 厉害!厉害! align(128)只是为了提高性能吧。如果不用的话,只是性能下降,没有其他副作用吧 有办法能关掉 Sleep 的align128吗,节省点空间

--
👇
catsalwaysmeow: 看起来是因为Sleep这个Future内部存在一个align(128)的字段。

Sleep->TimerEntry->TimerShared->StateCell->CachePadded

手动对状态机里的字段设置align(128)也能得到类似的效果,参考async_fn_aligned_structure

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=58ca2b8568f4a1a5336c0801789f7af1

catsalwaysmeow 2021-08-04 15:00

我也在学习async/await,还有许多东西没有弄明白,下面的理解可能有错哦。

编译器为async函数生成的匿名结构(返回的impl Future)储存了跨越了.await的变量(SoBigAField),以及必要的标志。原则上,它只是一个普通的结构,具体被放置在哪里取决于「调用者」如何处理它:

  • 如果「调用者」用Box::pin把它包了起来,那么它就在Allocator分配的内存中(通常是堆)。

  • 如果「调用者」只是将它赋值到了一个局部变量local_future中,分为两种情况。

    • 如果local_future在「调用者」的代码中被.await了,那么local_future会被放置在「调用者」返回的匿名结构中,它被放置在哪里又取决于「调用者」的「调用者」...
    • 如果local_future没有被.await,那么它会被放置到「调用者」的调用栈中。

--
👇
宁: 没注意,box::pin是一个方案,不用关Sleep的align,多谢多谢! 另外,我一直有一个疑问,future在运行的时候 async_fn_aligned_structure 里的 SoBigAField 是放在栈上的,还是在堆上的。

--
👇
宁: 厉害!厉害! align(128)只是为了提高性能吧。如果不用的话,只是性能下降,没有其他副作用吧 有办法能关掉 Sleep 的align128吗,节省点空间

--
👇
catsalwaysmeow: 看起来是因为Sleep这个Future内部存在一个align(128)的字段。

Sleep->TimerEntry->TimerShared->StateCell->CachePadded

手动对状态机里的字段设置align(128)也能得到类似的效果,参考async_fn_aligned_structure

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=58ca2b8568f4a1a5336c0801789f7af1

作者 2021-08-04 10:36

没注意,box::pin是一个方案,不用关Sleep的align,多谢多谢! 另外,我一直有一个疑问,future在运行的时候 async_fn_aligned_structure 里的 SoBigAField 是放在栈上的,还是在堆上的。

--
👇
宁: 厉害!厉害! align(128)只是为了提高性能吧。如果不用的话,只是性能下降,没有其他副作用吧 有办法能关掉 Sleep 的align128吗,节省点空间

--
👇
catsalwaysmeow: 看起来是因为Sleep这个Future内部存在一个align(128)的字段。

Sleep->TimerEntry->TimerShared->StateCell->CachePadded

手动对状态机里的字段设置align(128)也能得到类似的效果,参考async_fn_aligned_structure

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=58ca2b8568f4a1a5336c0801789f7af1

作者 2021-08-04 10:19

厉害!厉害! align(128)只是为了提高性能吧。如果不用的话,只是性能下降,没有其他副作用吧 有办法能关掉 Sleep 的align128吗,节省点空间

--
👇
catsalwaysmeow: 看起来是因为Sleep这个Future内部存在一个align(128)的字段。

Sleep->TimerEntry->TimerShared->StateCell->CachePadded

手动对状态机里的字段设置align(128)也能得到类似的效果,参考async_fn_aligned_structure

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=58ca2b8568f4a1a5336c0801789f7af1

catsalwaysmeow 2021-08-02 14:01

看起来是因为Sleep这个Future内部存在一个align(128)的字段。

Sleep->TimerEntry->TimerShared->StateCell->CachePadded

手动对状态机里的字段设置align(128)也能得到类似的效果,参考async_fn_aligned_structure

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=58ca2b8568f4a1a5336c0801789f7af1

1 共 11 条评论, 1 页