手把手理解C++20协程的编译实现

考虑下面的协程代码

#include <iostream>
#include <coroutine>

using namespace std;

class Resumable
{
   

};

Resumable func() {
   
    cout << "hello";
    co_await std::suspend_always();
    cout << " world";
}


int main()
{
   
    
}

编译报错

error: unable to find the promise type for this coroutine
   13 |     co_await std::suspend_always();
      |     ^~~~~~~~

为什么?

其实编译器在编译时,会希望生成如下的代码:

/* 经过编译器优化后的 func 函数 */
Resumable func()
{
   
    Frame *frame = operator new(size);	//	size = 函数形参大小 + 局部变量大小
    Rumable::promise_type promise;
    coroutine_handle *handle = coroutine_handle<>::from_promise(&promise);
    Resumable res = promise.get_return_object();	//	call the Resumable constructor 

    co_await promise.initial_suspend();	//	in some ways, this is a coroutine constructor
    try {
   
        //  func-body
        cout << "hello";
        co_await std::suspend_always();
        cout << " world";
        //	func-body end
    }catch (...) {
   
        promise.unhandled_exception();	//	coroutine exception handle
    }
    co_await promise.final_suspend();	//	in some ways, this is a coroutine destructor

    return res;
}

通过上面的代码,可以引出两个问题:

  1. 已知协程co_await可以完成上下文切换,那这个函数中co_await具体是怎么调用的?
  2. promise_type 哪里来?
  3. Resubmable如何实现?

同样,从上面的代码中可以推出,promise_type至少应该含有以下代码:

class promise_type
{
   
public:
    auto get_return_object();
    auto initial_suspend();
    void unhandled_exception();
    auto final_suspend();
    void return_void();
};

抱着上面三个问题,看看Resumable的实现规范。

Resumable的编译实现

class Resumable
{
   
public: /* 用户自定义实现部分 */
    class promise_type;	// 见上个代码块
    
	/* 
		用户的其他自定义实现代码
	*/

};

解决上面提出的问题:

  1. 已知协程co_await可以完成上下文切换,那这个函数中co_await具体是怎么调用的?

先继续存疑

  1. promise_type 哪里来?

答:从 Resumable 中由用户手动定义而来,且必须实现一些特定方法。

  1. Resubmable如何实现?

答:Resumable 必须包含 promise_type 子类型(typedef也算),其余没什么讲究。

再提出一些新问题:

  1. 协程如何将一个值从函数内co_await到函数外?
  2. 看起来Resumable在编译优化后的func里没有被用到,只在最后返回的时候return了一下,为什么不用promise直接代替Resumable?
    换句话说:为什么要给promise加一层外套作为返回类型?C++为什么要这样设计?

以下是未解决问题列表:

  1. 已知协程co_await可以完成上下文切换,那这个函数中co_await具体是怎么调用的?
  2. C++为什么要采用给promise加一层外套作为返回类型这样的设计方式?

总结一下

从上面可以看出,co_await 之类的协程关键字依然存在,这说明此处的编译优化并不是针对协程的,那为什么要这样做呢?

答案是为了更好的管理协程,可以看到,一次小小的协程函数调用覆盖了诞生、运行、错误处理、消亡等各个部分,这为将来高可用的框架奠定了基础,但对于写hello world的人不得不说,真***复杂。

C++有一个设计规范,叫做一个人只做一件事,在这里promise_type用来管理协程的生命周期。Resumable用来作为返回值。

如果说上面讲的都是协程规范的话,那么接下来要讲的部分就是具体协程的实现,看看 co_await 到底是如何调用的?

Awaitable对象的实现规范

回到最初的起点:

Resumable func() {
   
    cout << "hello";
    co_await std::suspend_always();
    cout << " world";
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要用Pytorch手写一个Transformer模型,可以按照以下步骤进行: 1. 导入所需的库和模块,包括torch、torch.nn、torch.nn.functional以及Transformer模型所需的子模块如EncoderLayer和DecoderLayer。 2. 定义Transformer模型的编码器部分。编码器由多个EncoderLayer组成,每个EncoderLayer包含自注意力机制(Self-Attention)、前馈神经网络和残差连接。 3. 定义Transformer模型的解码器部分。解码器也由多个DecoderLayer组成,每个DecoderLayer包含自注意力机制、编码器-解码器注意力机制和前馈神经网络。 4. 定义Transformer模型本身。它包含编码器和解码器,以及最后的线性层用于生成输出。 5. 实现模型的前向传播函数。在前向传播函数中,输入数据将分别经过编码器和解码器,并返回最后的输出。 6. 初始化模型并定义损失函数和优化器。 7. 定义训练循环。在每个训练迭代中,将输入数据传递给模型进行前向传播,计算损失值,并进行反向传播和参数更新。 8. 进行模型训练。根据实际情况,可以调整超参数、训练数据和训练次数等。 请注意,以上步骤是一个大致的框架,具体的实现细节可能会有所不同。可以参考引用中提到的huggingface提供的transformer模型代码,以及Transformer模型的论文《Attention is All You Need》来进行更详细的实现。 huggingface官方文档: [link] Transformer模型图: [link]<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [手把手教你用Pytorch代码实现Transformer模型(超详细的代码解读)](https://blog.csdn.net/qq_43827595/article/details/120394042)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值