IDL build
IDL build
|
The IDL build is a significant source of generated files, and a complex use of the build system (GYP or GN); see their documentation for possible issues.
Steps...
GYPComponents other than bindings do not need to know about the internal organization of the bindings build. Currently the components
core and modules depend on bindings, specifically bindings_core (in bindings/core) and bindings_modules (in bindings/modules), due to cyclic dependencies core ⇄ bindings_core and modules ⇄ bindings_modules. These should just include bindings/core/core.gypi or bindings/modules/modules.gypi, respectively, and depend on top-level targets, currently:bindings/core/v8/generated.gyp:bindings_core_v8_generatedbindings/modules/v8/generated.gyp:bindings_modules_v8_generatedGYP files (.gyp and .gypi) are arranged as follows:
.gyp: There's a generated.gyp file in each directory that generates files (outputting to
gen/blink/bindings/$dir), namely core, core/v8, modules, and modules/v8; these correspond to global info (core and modules) and actual V8 bindings (core/v8 and modules/v8). There's also scripts/scripts.gyp, for precaching in scripts (lexer/parser tables, compiling templates)..gypi: The .gypi files are named (as usual) as:
foo/foo.gypi # main, public: list of static files and configuration variablesfoo/generated.gypi # list of generated filesThere is one bindings-specific complexity, namely idl.gypi files (core/idl.gypi and modules/idl.gypi), due to having to categorize the IDL files for the build (see Web IDL interfaces: Build).
There are relatively long lists of .gypi includes in the .gyp files, which are due to factoring for componentization, and are longer than desired due to layering violations, namely
bindings_core → bindings_modules (Bug 358074): these make the (bad) dependencies explicit.PerformanceFor build performance in compiling a single IDL file, see IDL compiler: Performance
Build performance is one of the IDL compiler goals, though secondary to correctness and performance of the generated code.
The primary concern is minimizing full build time (notably full bindings generation and compilation); the secondary concern is maximizing incrementality of the build (only do full rebuilds when necessary). These priorities are because full builds are common (e.g., on build bots and for any compiler code changes), and changes to individual IDL files are also common, which should only rebuild that file's bindings and any dependents, not trigger a full rebuild.
Current performance (as of Blink r168630 in March 2014) is acceptable: on a fast Linux workstation with 16 cores, full regeneration takes ~3 seconds (~50 seconds of user time, ~80 ms/IDL file), and full rebuilds on IDL changes only occur if a dependency IDL file (partial interface or implemented interface) is touched, which is infrequent.
The coarsest way to profile a full build – which is sufficient for verifying coarse improvements – is to do a build (using ninja), then
touch idl_compiler.py and time(1) another build (most finely the targets should be bindings_core_v8_generated bindings_modules_v8_generated but (for ninja) it's fine for the target to be chrome); "user time" is most relevant. This should only rebuild the bindings, but not recompile or do any other steps, like linking. Note that build system (like ninja) has some overhead, so you want to compare to an empty build, and that touching some other files may trigger other steps.In the overall build, bindings compilation and linking are more significant factors, but are difficult to improve: minimizing includes and minimizing and simplifying generated code are the main approaches.
The main improvement would be precise computation of dependencies to minimize full rebuilds, so only dependent files are rebuild if a dependency is touched (Bug 341748), but this would require GYP changes.
DetailsIDL build performance contains various components:
Build performance criteria of the IDL build are the time for the following tasks:
The key techniques to optimize these are:
Compilation of multiple IDL files is embarrassingly parallel, as files are almost completely independent (code generation is independent, and reading in is independent other than reading in implemented or referenced interfaces, which is currently minor, and global information, which is handled in a separate step). Thus with enough cores or a distributed build, compilation is very fast: assuming one core per IDL file, full recompile should take 0.1 seconds, plus distribution overhead.
Further optimizationsThe main areas at the overall build level that suggest further optimization are as follows. These would not help significantly with full rebuilds, but would improve incrementality. These are thus possible, but not high priorities.
This would minimize full rebuilds, and decrease the size of generated build files: currently if a dependency IDL file (partial interface or implemented interface) is touched, all bindings are regenerated (conservatively). Instead, precise computation of dependencies would mean only the dependent files are rebuilt, so 1 or 2 files instead of 600. Further, this would reduce the size of the generated build files, because instead of the conservative list of about 60 dependencies x 600 main IDL files = 36,000 dependencies, this would have about 100 (some dependencies are implemented interfaces that are used multiple times); this would speed up build time due to lower overhead of reading and checking these dependencies. This would require new features in GYP, to allow per-file dependency computation in rules.
This would speed up incremental builds a bit, by minimizing the global computation, but may slow down full rebuilds significantly, due to launching O(n) processes.
Currently global computations are done in a single step, which processes all IDL files. Thus touching any IDL file requires reading all of them to recompute the global data. This could be replaced by a 2-step processes: compute the public data one file at a time (n processes), then have a consolidation step that reads these all in and produces the overall global data. Further, if the public data does not change (as is usually the case), the consolidation step need not run. This would thus speed up incremental builds. However, this would require n additional processes, so it would slow down full rebuilds.
CautionsCertain mistakes that significantly degrade build performance are easily made; all of these have either occurred or been proposed.
Be careful to use "write only on diff" (correctly) for all global dependency targets.
Touching a global dependency (a file or target that is a dependency of all IDL files) causes a full recompile: if a global dependency changes, all IDL files must be recompiled. Global dependency files are primarily the compiler itself, but also (for now) all dependency IDL files, since we compute dependencies conservatively in the build, not precisely. Global dependency targets (files generated in the preliminary steps) are primarily the global context, but also the generated global constructor IDL files, as these are partial interfaces (hence dependencies, hence treated as global dependencies). Touching any IDL file requires recomputing the global context and these generated global files. However, most changes to IDL files do not change the global dependencies (global context and global constructors), since they only change private information, and thus should not trigger full rebuilds. The "write only on diff" option, supported by some build systems, notably ninja, means that if these files do not change, we do not regenerate the targets, hence avoiding a full rebuild. If this is missed, touching any IDL file cause full regeneration.
Avoid launching processes per-IDL file: preferably only 1 process for the actual compilation, avoiding per-file caching steps.
Launching processes is relatively expensive, particularly on Windows. For global steps this is O(1) in the number of IDL files, so adding an extra global build step is cheap, but for compiling individual IDL files this is O(n), so it's faster to have 1 process per IDL file, i.e., compile each file in a single step. Thus any changes that add a process per IDL file are expensive. These include: splitting global computations into "compute per file one process at a time, then consolidate in one step" (turns O(n) computation in 1 process into O(n) computation in n + 1 processes); splitting the compile into "compute IR, then compile IR" (turns n processes into 2n processes).
Compute global data in a single preliminary step.
Computed data that is needed by more than one IDL file should generally be done a single time, most simply in the global context computation step (or a GYP variable); getting this wrong can turn O(n) work into O(n2) work. For example, public information about an interface should be computed once, rather than computing it every time it is used. However, in some cases redundant work is preferable to adding extra processes. For example, there is some redundancy in the front end, since some files (notably implemented interfaces) are read during the compile of several IDL files, but this is relatively rare (about 30/600=5% of files are read multiple times) and cheap (actual parsing is cheap relative to parser initialization), and fixing it would require a separate per-file caching step, which would increase the number of processes.
Use precise (not conservative) computations and static lists in GYP, and be careful of GYP rules.
GYP rules generate actions; for the IDL build this is the
binding rule in the individual_generated_bindings target, which generates one action per IDL file. Thus any work this does or data this generates is scaled by n. This is an issue for dependencies: the inputs are used for all generated actions, and thus currently include all dependency files, which generates O(nk) data (currently about 60 dependencies x 600 main IDL files = 36,000 input lines in the actions, instead of ~100 (some dependencies are implemented interfaces that are used multiple times)), which slows down both gyp runtime and the build (ninja) runtime, as it needs to read and check all these dependencies. Further, any O(n) work in the inputs, notably calling a Python script that runs a global computation, results in O(n2) work at gyp runtime.See also 350317: Reduce size of individual_generated_bindings.ninja.
Work can be reduces to O(1) if instead of using a Python script to compute dependencies dynamically (at gyp runtime), one determines the dependencies statically (when writing the
.gyp, .gypi files). This is a key reason that there are multiple variables with lists of IDL files: this allows gyp to just expand the variables, rather than running a computation (and obviates the need for a separate Python script).Rejected optimizationsSome intuitively appealing optimizations don't actually work well or at all; these are discussed here. These generally have little impact (or negative impact) and involve significant complexity in the build or compiler.
Compile multiple files in a single process (in sequence or via multiprocessing)Python and library initialization can be further sped up by processing multiple IDL files in a single compiler run (a single compilation process). However, this significantly complicates the build due to needing to manually distribute the input files to various build steps, ideally one per available core. This interacts poorly with GYP (GYP rules are designed for a single input file and the corresponding output), and is only useful if you cannot fully distribute the build, and thus is not done.
Note that multiprocessing or multithreading Python would not allow a single Python process to compile all files in parallel (thus minimizing initialization time), since the Python interpreter is not concurrency safe (hence not multithreaded; concretely this is due to the Global Interpreter Lock, GIL), and the actual parsing and templating are done in Python. A multiprocessing Python process could launch separate (OS) processes to compile individual files, but this offers no advantage over the build system doing it, and is more complex.
Fork multiple processesOne could start a single Python process, which then forks others once it has initialized to reduce startup time (both of Python and of the libraries): either one per input file or distributing among them. This allows both parallelization and minimal initialization, so it would increase speed. However, this is platform dependent (
fork() is not available on Windows, and would need to be replaced by spawn() or subprocess), and would move substantial build complexity into the compiler from the build system, hence avoided since this level of performance is not required.Cache IRThe IR is computed multiple times for a few IDL files, namely implemented interfaces that are implemented by several IDL interfaces and targets of
[PutForwards]. This is redundant work, so caching them would avoid this duplication. However, this would require splitting compilation into two steps – generate IR in one step, then compile IR in the second – and thus double the number of processes, which would slow down the build significantly, particularly on Windows. This can be worked around, at the expense of complicating the build, by only doing the caching for dependencies (or indeed only for implemented interfaces) and then checking for a cached IR file before reading the IDL file onself, but this is significant complexity for little benefit.Compute public dependencies preciselyCurrently if the global context changes, all bindings are generated. This is conservative and simple, which is why we do it this way, but it causes excess full rebuilds: if the public data of one IDL file changes (e.g., its
[ImplementedAs]), only bindings that actually depend on that public information (namely: use that interface) need to be rebuilt. Thus if IDL files depended on the public information of individual files, rather than on the global context, we could have more incremental rebuilds.This is not done because it would add significant complexity for little benefit (changes to public data are rare, and generally require a significant rebuild anyway), and would require GYP changes (above) for file-by-file dependency computation in rules.
References
|
IDL build的更多相关文章
- The IDL compiler
The IDL compiler or bindings generator transcompiles Web IDL to C++ code, specifically bindings betw ...
- 环境初始化 Build and Install the Apache Thrift IDL Compiler Install the Platform Development Tools
Apache Thrift - Centos 6.5 Install http://thrift.apache.org/docs/install/centos Building Apache Thri ...
- Build Instructions (Windows) – The Chromium Projects
转自:http://121.199.54.6/wordpress/?p=1156 原始地址:http://www.chromium.org/developers/how-tos/build-instr ...
- IDL和生成代码分析
IDL:接口描述语言 这里使用thrift-0.8.0-xsb这个版本来介绍IDL的定义以及简单实例分析. 1. namespace 定义包名 2.struct 结构体,定义服务接口的参数和返回值用到 ...
- Java调用IDL出错处理
之前有一个java调用idl的详细介绍http://www.cnblogs.com/lizhishan3380/p/4353286.html,里面有提到[需要先在java中加载IDL的java包(ja ...
- Java调用IDL方法总结
Java调用IDL方法总结 Java调用IDL程序,需要先在java中加载IDL的java包(javaidlb.jar),该包不需要下载,在IDL的安装目录中可以直接找到(C:\Program Fil ...
- Mojom IDL and Bindings Generator
Mojom IDL and Bindings Generator This document is a subset of the Mojo documentation. Contents Overv ...
- 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用
问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...
- [WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform
eclipse maven clean install 报错 1. 修改properties-->resource-->utf-8仍然报错 2.修改项目pom.xml文件,增加: < ...
随机推荐
- jquery/zepto在插件编写上的几点区别
1. 自定义事件的命名空间 jq的时间命名空间是用点“.”,而zepto是用冒号“:” 如 //jquery $(this).trigger('cusevent.pluginname'); //zep ...
- Swift学习笔记(10):类和结构体
目录: 基本 属性 方法 下标 继承 基本 使用class和struct关键字定义类和结构体. ・类是引用类型,结构体和枚举是值类型 ・值类型被赋予给一个变量.常量或被传递给一个函数时,已值拷贝方式传 ...
- 中文版 R-FCN: Object Detection via Region-based Fully Convolutional Networks
R-FCN: Object Detection via Region-based Fully Convolutional Networks 摘要 我们提出了基于区域的全卷积网络,以实现准确和高效的目标 ...
- 51nod 1268 和为K的组合 dfs
题目: 1268 和为K的组合 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K.如果可以,输出:& ...
- swift语言点评三 - Basic Operators
1.Tuples are compared from left to right, one value at a time, until the comparison finds two values ...
- Mac 如何修改Mac系统的默认截图路径
step 1 :打在桌面或者其他任意位置创建一个文件夹:截图图库.我创建的路径是:/Users/yilin/Documents/截图图库(仅供参考) step 2:打开终端,输入以下命令:defaul ...
- seq去除重复数据
DELETE FROM temp_fjh_2 a WHERE a.rowid!=(SELECT MAX(b.rowid) FROM temp_fjh_2 b WHERE a.a=b.a); 表名和列名 ...
- 虚拟机创建后该如何获取IP地址并访问互联网实用教程
之前在做项目的时候主机IP地址.网关.DNS.子网掩码等都是公司或者对方直接给提供的,但是如果我们自己想搭建一台虚拟机或者一台集群的话,手头又没有IP地址,该肿么办呢? 白慌,这里介绍一个小技巧, ...
- split方法切割数组
指定的字符串按"o"截取 当一个base64需要剪去前面的部分的时候 var params={ "imgJustBase64":this.zheng.split ...
- 搭建ss总结
今天晚上做的事情: 1. https://www.vultr.com/ 购买vps 2. ssh连接到服务器 参照网上帖子安装 https://blog.csdn.net/littlepig19930 ...