0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在BuildRelay中会调用Codegen函数

电子设计 来源:电子设计 作者:电子设计 2022-02-08 16:02 次阅读

作者:安平博,Xilinx高级工程师;来源:AI加速微信公众号

接着上一章继续深入代码,在BuildRelay中会调用Codegen函数。这个函数实现在src/relay/backend/graph_runtime_codegen.cc中。Codegen实现了内存的分配,IR节点到TIR节点的转换,tir图节点的一个调度优化。内存分配由函数relay.backend.GraphPlanMemory来实现,VisitExpr对节点进行遍历并进行节点信息的记录。LowerExternalfunctions完成ir节点到tir节点的转化以及schedule的优化。

pIYBAGAJmYKAeKUpAAOkJNgC9IE642.png

内存分配

通过GetPackedFunc函数来获得注册到global map的内存分配函数GraphPlanMemory。我们看一下文件src/relay/backend/graph_plan_memory.cc中对内存的处理。

o4YBAGAJmcKARl1CAACGZg0dj7U340.png

在处理内存分配中主要使用了StorageAllocaBaseVisitor,StorageAllocaInit,StorageAllocator这三个类。StorageAllocaBaseVisitor是一个基类,实现了对每个节点的访问,并分配token,但是token中信息是在派生类中处理的。定义了一个StorageToken的结构体,用于表示申请到内存的大小,类型等信息。在内存处理程序中,主要就是为每个节点分配这个token,同时定义token的内部信息。内存分配结果是一个节点和token的映射表。

o4YBAGAJmgGAEqo-AADcglyV-4Y415.png

StorageAllocator类中Plan函数为:

o4YBAGAJmkGASccNAAKmVvtXPgY414.png

关键是前两行代码,第一行代码初始化了storageToken,赋予了其设备类型和数据类型信息。第二行代码遍历每个节点,并且为每个节点分配内存空间。在内存初始化函数GetInitTokenMap中,首先收集每个节点的的设备信息。调用链为CollectDeviceInfo -> GetDeviceMap(src/relay/transforms/device_annotation.cc)。在构建relay图结构的时候,每个节点是有设备号信息的,GetDeviceMap就是按照post-DFS顺序获得节点的设备号信息。当然并不是所有节点都有设备号信息,所以还需要根据节点之间的关系来推断出设备号。比如下图,add,sqrt,log节点被标注为1,2,3号设备,那么可以用两种方式来推断其它节点设备号。

1) 从一个copy节点由下而上遍历一直到遇到下一个copy,比如可以推断出add,x,y节点的设备号和copy1一样;
2) 从最后一个copy节点向下遍历,那么可以推断出substract,exp设备号和copy3一样。

pIYBAGAJmpqAIGavAACO4hsCsQ8586.png

设备号获得后,this->run会调用基类的run函数,基类run函数会调用派生类的CreateToken函数。CreateToken会申请StorageToken空间并且赋予设备号和数据类型,然后返回一个token_map_。和节点遍历相关函数为Run->GetToken->VisitExpr。VisitExpr会最终调用StorageAllocaInit类中定义的VisitExpr_函数来遍历节点。

节点内存初始化完成后,回到StorageAllocator类中,run会调用其定义的CreateToken函数。

pIYBAGAJmt6AE8_gAALBr5QvaFI549.png

分配内存空间会有两种情况,一种是can_realloc一种是不能can_realloc的。先看不can_realloc的,GetMemorySize是根据token中记录的数据类型和shape信息来获得数据的大小,Alloc函数就是为tok分配字节数量。现在看can_realloc的情况,Request中首先获取节点数据的大小。然后从free_中查询能够满足size的节点,如果有比该节点size大的就选择大的空闲区间分配,如果没有大的空间分配,选择最接近的空间分配。然后最终返回一个token_map_。

codegen

第一步是对ir节点进行遍历,转换成codegen中定义的基础节点。我们先看以下codegen中定义的节点类型,GraphNode是基础节点,GraphInputNode, GraphOpNode继承自这个基础节点。这些节点中主要提供了一些节点属性,比如name,op类型等。还提供了dmlc接口,可以实现可视化。

遍历func的parameters,将parameters转换到graph的input节点。通过AddNode添加这些input节点,并且将转换后的graphInputNode加入var_map_中,var_map_中是expr到graphNode的映射。

接下来是节点遍历,heads_=VisitExpr(func->body)。节点遍历过程中会将func中的节点转换为graphNode。对于varNode,因为已经记录在var_map_中,直接返回引用。ConstantNode会转换为GraphInputNode,tuppleNode会返回每个字段的graphNode。在遍历节点过程中,会将graphNode都添加到nodes_中。

重点看一下对CallNode的处理,只支持op是functionNode类型的。

pIYBAGAJmx6AF101AAGj_ZQNbQ8072.png

Function生成时,走两个分支,一个是外部codegen,一个是通用分支。对应外部function codegen的处理为:

pIYBAGAJm2CAfy9BAANCOTr1_2U471.png

首先创建一个CCacheKey类型作为_CompileEngineLower函数的参数传入。具体CcacheKey有什么作用,以后再深入研究吧。_CompileEngineLower的实现在文件src/relay/backend/compile_engine.cc中。调用链为Lower -> LowerInternal(key)->cached_func。定义了一个cache_node并封装成cached_func返回。这块具体的操作并不是很理解,可能还需要熟悉cachedFuncNode的作用。

o4YBAGAJm7yAUjp_AAKzrtE_tdQ078.png

然后通过GraphAddCallNode将其加入nodes_中。在GraphAddCallNode中还会对op->args进行深入遍历。

内部func处理如下:

o4YBAGAJm_uALky3AAGAfeWt9Xs683.png

也是通过相同的pf0和pf1函数。CcacheKey的创建过程一样,但是在lowerInternal中不一样。

o4YBAGAJnD6ATZm6AAStQTbokG4194.png

首先创建了一个schedule,schedule的具体实现很复杂目前还不够理解。

如果是copy节点,那么不进行lower处理,直接返回CachedFunc封装。不是copy节点,如果我们在python中自己定义了lower函数就调用python中的,如果没有就会调用TVM中的lower函数。Lower函数在src/driver/driver_api.cc文件中。在这里调用了很多tir的passes来进行一个节点转换。这块后边再详细看。

审核编辑:何安

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4276

    浏览量

    62314
收藏 人收藏

    评论

    相关推荐

    RTOS V1.4版本SDK作为TCP Server没有调用断开连接的回调函数,为什么?

    espconn_regist_connectcb所注册的函数 espconn_regist_connectcb所注册的函数里面调用espconn_regist_disconcb
    发表于 07-18 08:31

    websocket.c RTOS演示中缺少对wifi_connect()的调用怎么办?

    RTOS SDK 1.3 中,有一个名为 /examples/websocket_demo/websocket/websocket.c 的示例。函数中有一个名为 websocket_task
    发表于 07-18 06:37

    ESP8266收到重传的UDP数据包,则udp接收回调函数会调用两次,怎么解决?

    我们使用 esp8266 开发了一个网格系统。 在所有开发完成时,我们发现了一个关键问题。 如果ESP8266收到重传的 UDP 数据包,则 udp 接收回调函数会调用两次。 (*. 收到两个
    发表于 07-18 06:29

    点击j-link下载之后,不会调用出j-link.exe,没有反应怎么解决?

    点击j-link下载之后,不会调用出j-link.exe,没有反应,有大神遇到这种情况吗,怎么解决,求助
    发表于 07-18 06:12

    ESP32S2使用VSCODE编译,总是会调用build->bootLoader->config->sdkconfig.h文件为什么?

    ESP32S2使用VSCODE编译,调用sdkconfig.h头文件时,总是会调用 build->bootLoader->config->
    发表于 06-07 06:48

    FreeRTOS如何在中断中调用内存分配函数

    最近在玩FreeRTOS,遇到一个问题,就是不知如何在中断中调用内存分配函数。pvPortMalloc函数中会调用xTaskResumeAll,而这个
    发表于 05-08 08:25

    函数多层调用的主要注意事项分析

    应用方案设计中,开发者经常会碰到某个子函数需要多次多级调用的情况。
    的头像 发表于 03-27 15:36 641次阅读
    子<b class='flag-5'>函数</b>多层<b class='flag-5'>调用</b>的主要注意事项分析

    linux用gdb调试遇到函数调用怎么办?

    linux用gdb调试遇到函数调用怎么办? Linux上使用GDB调试时,遇到函数调用是一个常见的情况。
    的头像 发表于 01-31 10:33 669次阅读

    Linux内核中信号相关的系统调用

    ,sys_sigaction()和sys_rt_sigaction()几乎相同,因此C库中包含的sigaction()包装函数最终会调用sys_rt_sigaction()而不是sys_sigaction()。
    的头像 发表于 01-20 09:34 613次阅读

    python调用math函数的方法

    Python编程中,数学函数是非常重要的工具,我们可以使用它们进行各种数值计算、几何运算和统计分析等操作。Python的标准库中内置了很多数学函数,而其中最基本和常用的数学函数被封装
    的头像 发表于 11-22 11:01 2657次阅读

    append()和insert()函数的区别

    。 append() 函数用于列表的末尾添加元素。当我们调用 append() 函数时,元素将会被添加到列表的最后一个位置。例如: my_list = [ 1 , 2 , 3 ]my
    的头像 发表于 11-21 14:44 2626次阅读

    我们应如何查看某一次函数调用时的内部变量呢?

    模块化设计的思想是把一些相似的功能(比如电机控制、阀控制)设计成函数函数块,这样就可以反复调用
    的头像 发表于 11-17 09:09 441次阅读
    我们应如何查看某一次<b class='flag-5'>函数</b>块<b class='flag-5'>调用</b>时的内部变量呢?

    如何查看及更改函数/函数块的调用环境

    模块化设计的思想是把一些相似的功能(比如电机控制、阀控制)设计成函数函数块,这样就可以反复调用。其优点是:使程序架构更加清晰,避免重复编写相似功能的代码。不过可能会产生一个疑惑:既然PLC的程序
    的头像 发表于 11-17 09:08 825次阅读
    如何查看及更改<b class='flag-5'>函数</b>/<b class='flag-5'>函数</b>块的<b class='flag-5'>调用</b>环境

    mallocLinux上执行的是哪个系统调用

    malloc底层为什么是内存池 malloc大家都用过,其是库函数。我们都知道库函数不同的操作系统中其实执行的是系统调用,那么malloc
    的头像 发表于 11-13 10:36 914次阅读
    malloc<b class='flag-5'>在</b>Linux上执行的是哪个系统<b class='flag-5'>调用</b>

    什么是远程过程调用

    is_exist(int friend_id)判断用户是否在线 根据结果在决议是发送在线消息还是离线消息。 那么对于一个继承了登录和聊天功能的系统,我们本地调用一个函数,就直接返回值=函数
    的头像 发表于 11-10 10:10 959次阅读
    什么是远程过程<b class='flag-5'>调用</b>