PyTorch檔案生成機制中的FileManager.write_with_template

PyTorch檔案生成機制中的FileManager.write_with_template

  • 前言
  • FileManager.write_with_template調用
    • gen_pyi
    • gen_nn_functional
    • write_sharded
  • FileManager.write_with_template實現
    • torchgen/utils.py
      • FileManager.write_with_template
      • FileManager.substitute_with_template
      • _read_template
    • torchgen/code_template.py
      • CodeTemplate
      • CodeTemplate.from_file
      • CodeTemplate.\__init__
      • substitute

前言

PyTorch中有些檔案是在編譯過程中跑腳本生成的,如.pyi檔是由.pyi.in檔生成,torch/csrc/autograd/generated目錄下的.cpp檔則是由tools/autograd/templates下的template .cpp檔生成的。

它們底層都是調用FileManager.write_with_template函數,其功能是對原檔案中的特定字串依照callback function所指示的方式做替換,進而生成對應的.pyi.cpp檔。

本文會先查看FileManager.write_with_template函數是如何被調用的,再細看它的實現。

FileManager.write_with_template調用

gen_pyi

tools/pyi/gen_pyi.py

    fm.write_with_template("torch/_C/__init__.pyi","torch/_C/__init__.pyi.in",lambda: {"generated_comment": "@" + "generated from torch/_C/__init__.pyi.in",**env,},)fm.write_with_template("torch/_C/_VariableFunctions.pyi","torch/_C/_VariableFunctions.pyi.in",lambda: {"generated_comment": "@"+ "generated from torch/_C/_VariableFunctions.pyi.in",**env,},)fm.write_with_template("torch/_VF.pyi","torch/_C/_VariableFunctions.pyi.in",lambda: {"generated_comment": "@"+ "generated from torch/_C/_VariableFunctions.pyi.in",**env,},)fm.write_with_template("torch/return_types.pyi","torch/_C/return_types.pyi.in",lambda: {"generated_comment": "@" + "generated from torch/_C/return_types.pyi",**env,},)gen_nn_functional(fm)

此處的四個fm.write_with_template會由torch/_C資料夾下的四個.pyi.in檔生成torch/_C資料夾下的__init__.pyi, _VariableFunctions.pyitorch資料夾下的_VF.pyi, return_types.pyi

gen_nn_functional

tools/pyi/gen_pyi.py

def gen_nn_functional(fm: FileManager) -> None:# ...fm.write_with_template("torch/nn/functional.pyi","torch/nn/functional.pyi.in",lambda: {"imported_hints": import_code,"dispatched_hints": dispatch_code,},)# ...fm.write_with_template("torch/_C/_nn.pyi","torch/_C/_nn.pyi.in",lambda: {"imported_hints": import_code,"dispatched_hints": dispatch_code,},)

此處的兩個fm.write_with_template會由torch/nn/functional.pyi.intorch/_C/_nn.pyi.in生成torch/nn/functional.pyitorch/_C/_nn.pyi.in

write_sharded

torchgen/utils.py

    def write_sharded(self,filename: str,items: Iterable[T],*,key_fn: Callable[[T], str],env_callable: Callable[[T], Dict[str, List[str]]],num_shards: int,base_env: Optional[Dict[str, Any]] = None,sharded_keys: Set[str],) -> None:#...for shard in all_shards:shard_id = shard["shard_id"]self.write_with_template(f"{base_filename}{shard_id}{extension}", filename, lambda: shard)#...

其中的all_shards為:

[{'shard_id': 'Everything'}, {'shard_id': '_0'}, {'shard_id': '_1'}, {'shard_id': '_2'}]

所以這裡的write_with_template會由filenamepython_torch_functions.cpp生成python_torch_functionsEverything.cpp, python_torch_functions_0.cpp, python_torch_functions_1.cpppython_torch_functions_2.cpp四個檔案。

注意到上面三個例子中,write_with_template的第三個參數(env_callable)都是一個呼叫後會返回dict的lambda函數。

FileManager.write_with_template實現

torchgen/utils.py

FileManager.write_with_template

write_with_template除了self以外有三個參數:

  • filename:生成的.pyi的檔名或.cpp的檔名
  • template_fn:作為輸入的.pyi.in的檔名或template .cpp的檔名
  • env_callable:在做替換時會用到的callback function
    def write_with_template(self,filename: str,template_fn: str,env_callable: Callable[[], Union[str, Dict[str, Any]]],) -> None:filename = "{}/{}".format(self.install_dir, filename)assert filename not in self.filenames, "duplicate file write {filename}"self.filenames.add(filename)if not self.dry_run:substitute_out = self.substitute_with_template(template_fn=template_fn,env_callable=env_callable,)self._write_if_changed(filename=filename, contents=substitute_out)

可以看到這段代碼最核心的內容就是調用substitute_with_template生成substitute_out

之後再將替換後的結果,也就是substitute_out寫入filename.pyi檔)這個檔案中。

注:在做類型檢查時,callback function是由typing.Callable表示的,詳見Python typing函式庫和torch.types。

FileManager.substitute_with_template

torchgen/utils.py

self外有兩個參數:

  • template_fn:作為輸入的.pyi.in的檔名或template .cpp的檔名
  • env_callable:在做替換時會用到的callback function
    # Read from template file and replace pattern with callable (type could be dict or str).def substitute_with_template(self, template_fn: str, env_callable: Callable[[], Union[str, Dict[str, Any]]]) -> str:template_path = os.path.join(self.template_dir, template_fn)env = env_callable()if isinstance(env, dict):# TODO: Update the comment reference to the correct locationif "generated_comment" not in env:comment = "@" + "generated by torchgen/gen.py"comment += " from {}".format(os.path.basename(template_path))env["generated_comment"] = commenttemplate = _read_template(template_path)return template.substitute(env)elif isinstance(env, str):return envelse:assert_never(env)

env_callable是一個呼叫後會返回dict的lambda函數,所以會進入isinstance(env, dict)這個分支,先由_read_template讀入template檔案(.pyi.in檔或template .cpp檔)後調用template.substitute

_read_template

torchgen/utils.py

參數template_fnpyi或template cpp的檔名。

@functools.lru_cache(maxsize=None)
def _read_template(template_fn: str) -> CodeTemplate:return CodeTemplate.from_file(template_fn)

讀入template_fn,生成CodeTemplate物件並回傳。

torchgen/code_template.py

CodeTemplate

torchgen/code_template.py

先來看看CodeTemplate類別的作用。

# match $identifier or ${identifier} and replace with value in env
# If this identifier is at the beginning of whitespace on a line
# and its value is a list then it is treated as
# block substitution by indenting to that depth and putting each element
# of the list on its own line
# if the identifier is on a line starting with non-whitespace and a list
# then it is comma separated ${,foo} will insert a comma before the list
# if this list is not empty and ${foo,} will insert one after.class CodeTemplate:substitution_str = r"(^[^\n\S]*)?\$([^\d\W]\w*|\{,?[^\d\W]\w*\,?})"substitution = re.compile(substitution_str, re.MULTILINE)pattern: strfilename: str# ...

注釋裡說明了CodeTemplate的功用是把模板中${identifier}字樣替換成env中對應的value。

torch/_C/_VariableFunctions.pyi.in中就有以下字樣:

# ${generated_comment}
# ...
${function_hints}${all_directive}

python_torch_functions.cpp中則有以下字樣:

#ifndef AT_PER_OPERATOR_HEADERS
#include <ATen/Functions.h>
#else
$ops_headers
#endif// ...
// generated forward declarations start here${py_forwards}// ...
static PyMethodDef torch_functions_shard[] = {${py_method_defs}
};// ...
// generated methods start here${py_methods}

CodeTemplate.from_file

torchgen/code_template.py

class CodeTemplate:# ...@staticmethoddef from_file(filename: str) -> "CodeTemplate":with open(filename, "r") as f:return CodeTemplate(f.read(), filename)# ...

調用CodeTemplate的建構子,傳入filename的內容及名稱。

CodeTemplate._init_

  • filename:作為輸入的.pyi.in的檔名或template .cpp的檔名
  • pattern:在CodeTemplate.from_file中是以CodeTemplate(f.read(), filename)調用CodeTemplate建構子,所以pattern成員變數會被設為從filename檔案裡讀出來的東西
class CodeTemplate:# ...def __init__(self, pattern: str, filename: str = "") -> None:self.pattern = patternself.filename = filename# ...

substitute

torchgen/code_template.py

回顧torchgen/utils.pysubstitute_with_template中的:

            template = _read_template(template_path)

生成了CodeTemplate物件template後繼續調用:

            return template.substitute(env)

其功能是做一些正則替換:

class CodeTemplate:# ...def substitute(self, env: Optional[Mapping[str, object]] = None, **kwargs: object) -> str:if env is None:env = {}def lookup(v: str) -> object:assert env is not Nonereturn kwargs[v] if v in kwargs else env[v]def indent_lines(indent: str, v: Sequence[object]) -> str:return "".join([indent + l + "\n" for e in v for l in str(e).splitlines()]).rstrip()def replace(match: Match[str]) -> str:indent = match.group(1)key = match.group(2)comma_before = ""comma_after = ""if key[0] == "{":key = key[1:-1]if key[0] == ",":comma_before = ", "key = key[1:]if key[-1] == ",":comma_after = ", "key = key[:-1]v = lookup(key)if indent is not None:if not isinstance(v, list):v = [v]return indent_lines(indent, v)elif isinstance(v, list):middle = ", ".join([str(x) for x in v])if len(v) == 0:return middlereturn comma_before + middle + comma_afterelse:return str(v)return self.substitution.sub(replace, self.pattern)

函數最後的self.substitution.sub(replace, self.pattern)中的self.substitutionCodeTemplate的成員:

    substitution_str = r"(^[^\n\S]*)?\$([^\d\W]\w*|\{,?[^\d\W]\w*\,?})"substitution = re.compile(substitution_str, re.MULTILINE)

re.compile後得到的substitution是一個re.Pattern物件。

先來看看re.Pattern.sub是什麼,參考Passing a function to re.sub in Python及Python: re.compile and re.sub中給出的例子:

import re
substitution = re.compile(r'\d')
number_mapping = {'1': 'one', '2': 'two', '3': 'three'}
s = "1 testing 2 3"
substitution.sub(lambda x: number_mapping[x.group()], s) # 'one testing two three'

re.Pattern.sub的第一個參數是做替換的函數,第二個參數則是欲處理的字串,它會尋找特定樣式的字串(此處是r'\d'),對它們做替換後回傳。

所以self.substitution.sub(replace, self.pattern)這句是在self.pattern(也就是pyi.in或template cpp檔中的內容)中尋找substitution_str樣式的字串,並用replace這個函數所指定的方式做替換。

得到替換後的結果後,回到substitute_with_template函數:

            return template.substitute(env)

那裡繼續將結果回傳,來到write_with_template函數:

            substitute_out = self.substitute_with_template(template_fn=template_fn,env_callable=env_callable,)self._write_if_changed(filename=filename, contents=substitute_out)

在那裡會把替換結果substitute_out寫入filename,也就是生成的.pyi的檔名或.cpp的檔名。

來看看torch/_C/_VariableFunctions.pyi中的${generated_comment}

回顧gen_pyi函數中呼叫write_with_template時,與env一同傳入了generated_comment的key value pair:

    fm.write_with_template("torch/_C/_VariableFunctions.pyi","torch/_C/_VariableFunctions.pyi.in",lambda: {"generated_comment": "@"+ "generated from torch/_C/_VariableFunctions.pyi.in",**env,},)

所以到了substitute函數,env參數便是一個包含generated_comment的key value pair的字典。

# ${generated_comment}在做替換後,會變成生成的torch/_C/_VariableFunctions.pyi檔案中的第一行:

# @generated from torch/_C/_VariableFunctions.pyi.in

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/61627.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java八股文面试[多线程]——线程池拒绝策略

四种线程池拒绝策略&#xff08;handler&#xff09; 当线程池的线程数达到最大线程数时&#xff0c;需要执行拒绝策略。拒绝策略需要实现 RejectedExecutionHandler 接口&#xff0c;并实现 rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法。不过…

如何设计微服务

一、序幕 最近在思考&#xff0c;自己哪些不足&#xff0c;需要学习点什么&#xff1f;看着Java基础知识&#xff0c;千遍一律&#xff0c;没有太大的动力需深挖&#xff0c;只能在写业务项目的时候边写边思考边夯实自己的基础。于是看了网上的一些资料&#xff0c;结合以前面试…

J1元器件的功能与应用 | 百能云芯

在现代科技和电子领域中&#xff0c;元器件是构建各种电子设备的基石。其中&#xff0c;J1元器件作为一个备受关注的焦点&#xff0c;在电子工程师和科技爱好者中引发了浓厚的兴趣。百能云芯将带您深入了解J1元器件在电子世界中的作用。 J1元器件是一种通用的连接器&#xff0c…

【深度学习实验】NumPy的简单用法

目录 一、NumPy介绍 1. 官网 2. 官方教程 二、实验内容 1. 导入numpy库 2. 打印版本号 3. arange 函数 4. array函数 5. reshape函数 6. 矩阵点乘&#xff08;逐元素相乘&#xff09; 7. 矩阵乘法 一、NumPy介绍 NumPy是一个常用于科学计算的Python库&#xff0c;尤…

Ubuntu系统下配置 Qt Creator 输入中文、配置软件源的服务器地址、修改Ubuntu系统时间

上篇介绍了Ubuntu系统下搭建QtCreator开发环境。我们可以发现安装好的QtCreator不能输入中文&#xff0c;也没有中文输入法供选择&#xff0c;这里需要进行设置。 文章目录 1. 配置软件源的服务器地址2. 先配置Ubuntu系统语言&#xff0c;设置为中文3. 安装Fcitx插件&#xff…

初始化列表

文章目录 一. 初始化列表是什么&#xff1f;二. 为什么要有初始化列表&#xff1f;三. 初始化列表的特性四. explicit关键字五. statis成员六. 友元七. 内部类八. 匿名对象九. 编译器优化总结&#xff1a; 一. 初始化列表是什么&#xff1f; 初始化列表是构造函数真正初始化的地…

Guava RateLimiter限流

令牌桶算法 令牌桶是按照固定速率往桶中添加令牌&#xff0c;请求是否被处理需要看桶中令牌是否足够&#xff0c;当令牌数减为零时则拒绝新的请求&#xff1b;漏桶则是按照常量固定速率流出请求&#xff0c;流入请求速率任意&#xff0c;当流入的请求数累积到漏桶容量时&#…

ctfshow 红包题

前言&#xff1a; 最近一直在搞java很少刷题&#xff0c;看见ctfshow的活动赶紧来复现一波~ ctfshow 红包挑战7 <?php highlight_file(__FILE__); error_reporting(2); extract($_GET); ini_set($name,$value); system("ls ".filter($_GET[1])."" )…

集合框架-(Collection/Map)

1.单列集合 1.1基础概要 集合中存储的是对象的地址信息&#xff0c;想要输出对象的信息&#xff0c;需要在具体的类中重写toString&#xff08;&#xff09;方法 Collection代表单列集合&#xff0c;每个元素数据只包含一个值 List集合&#xff1a;添加的元素可以是有序、可…

_kbhit() and getch() 在小游戏中用不了。因为控制台函数,仅在控制台程序中可用

太长不看版&#xff1a; _kbhit() and getch() 包含在conio.h中。 conio是Console Input/Output&#xff08;控制台输入输出&#xff09;的简写&#xff0c;其中定义了通过控制台进行数据输入和数据输出的函数&#xff0c;主要是一些用户通过按键盘产生的对应操作&#xff0c…

ZooKeeper技术内幕

文章目录 1、系统模型1.1、数据模型1.2、节点特性1.2.1、节点类型 1.3、版本——保证分布式数据原子性操作1.4、 Watcher——数据变更的通知1.5、ACL——保障数据的安全1.5.1、权限模式&#xff1a;Scheme1.5.2、授权对象&#xff1a;ID1.5.3、权限扩展体系 2、序列化与协议2.1…

【狂神】Spring5笔记(1-9)

目录 首页&#xff1a; 1.Spring 1.1 简介 1.2 优点 2.IOC理论推导 3.IOC本质 4.HelloSpring ERROR 5.IOC创建对象方式 5.1、无参构造 这个是默认的 5.2、有参构造 6.Spring配置说明 6.1、别名 6.2、Bean的配置 6.3、import 7.DL依赖注入环境 7.1 构造器注入 …

Pydantic 学习随笔

这里是零散的记录一些学习过程中随机的理解&#xff0c;因此这里的记录不成体系。如果是想学习 Pydantic 建议看官方文档&#xff0c;写的很详细并且成体系。如果有问题需要交流&#xff0c;欢迎私信或者评论。 siwa 报 500 Pydantic 可以和 siwa 结合使用&#xff0c;这样既…

hyperf 十五 验证器

官方文档&#xff1a;Hyperf 验证器报错需要配合多语言使用&#xff0c;创建配置自动生成对应的语言文件。 一 安装 composer require hyperf/validation:v2.2.33 composer require hyperf/translation:v2.2.33php bin/hyperf.php vendor:publish hyperf/translation php bi…

React和Redux中的不变性

https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/ 一、不变性和副作用 1.不变&#xff1a;不断创造新值来替换旧值 2.不变性规则&#xff1a; &#xff08;1&#xff09;当给定相同的输入时&#xff0c;纯函数必须始终返回相同的值 &#xff08;2&#xff0…

如何利用Python代码优雅的进行文件下载

如何利用Python代码优雅的进行文件下载 一、什么是wget&#xff1f;二、使用wget.exe客户端进行文件下载三、使用Python脚本进行文件下载 欢迎学习交流&#xff01; 邮箱&#xff1a; z…1…6.com 网站&#xff1a; https://zephyrhours.github.io/ 一、什么是wget&#xff1f;…

JavaWeb_LeadNews_Day9-Redis实现用户行为

JavaWeb_LeadNews_Day9-Redis实现用户行为 网关配置点赞阅读不喜欢关注收藏文章详情-行为数据回显来源Gitee 网关配置 nacos: leadnews-app-gateway # 用户行为微服务 - id: leadnews-behavioruri: lb://leadnews-behaviorpredicates:- Path/behavior/**filters:- StripPrefi…

yolov3

yolov1 传统的算法 最主要的是先猜很多候选框&#xff0c;然后使用特征工程来提取特征&#xff08;特征向量&#xff09;,最后使用传统的机器学习工具进行训练。然而复杂的过程可能会导致引入大量的噪声&#xff0c;丢失很多信息。 从传统的可以总结出目标检测可以分为两个阶…

Java 读取TIFF JPEG GIF PNG PDF

Java 读取TIFF JPEG GIF PNG PDF 本文解决方法基于开源 tesseract 下载适合自己系统版本的tesseract &#xff0c;官网链接&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/ 2. 下载之后安装&#xff0c;安装的时候选择选择语言包&#xff0c;我选择了中文和英文 3.…

提高Python并发性能 - asyncio/aiohttp介绍

在进行大规模数据采集时&#xff0c;如何提高Python爬虫的并发性能是一个关键问题。本文将向您介绍使用asyncio和aiohttp库实现异步网络请求的方法&#xff0c;并通过具体结果和结论展示它们对于优化爬虫效率所带来的效果。 1. 什么是异步编程&#xff1f; 异步编程是一种非阻…