热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[Erlang0029]ErlangInline编译

内联函数建议编译器将制定的函数体插入并取代每一处调用该函数的地方,从而节省了每一次函数调用带来的时间开支,选择内联函数时,必须要在程序占用空间和程序执行效率之间进行权衡,因为过多的

  内联函数建议编译器将制定的函数体插入并取代每一处调用该函数的地方,从而节省了每一次函数调用带来的时间开支,选择内联函数时,必须要在程序占用空间和程序执行效率之间进行权衡,因为过多的对较为复杂的函数进行内联扩展将带来很大的存储资源开支.另外注意对于递归函数的内联扩展可能带来部分编译器的无穷编译.内联扩展是一种特别的用于消除调用函数时所造成的固有的时间消耗方法。一般用于能够快速执行的函数,因为在这种情况下函数调用的时间消耗显得更为突出。
                                                                                                                                                                            --维基百科内联函数摘要

从维基百科的描述中可以看到内联函数解决的问题是:函数的调用时间比函数执行时间相当的时候,通过空间换时间,获得执行效率.实现角度Inlining是通过代码复制的方式节省进栈出栈的开销.Erlang的编译器可以将Erlang模块中的函数进行内联编译,内联(inlining)的含义是把一个方法的调用替换成函数体并把参数替换成实际值.
Erlang内联不是默认值;必须明确指定compile选项( 形式: {inline,[{Name,Arity},...]} ) 或者在源代码使用-compile.

%%Example of explicit inlining:

-compile({inline,[pi/0]}).

pi() -> 3.1416.


%% Example of implicit inlining:

-compile(inline).


%% Aggressive inlining - will increase code size.

-compile(inline).

-compile({inline_size,100}).

如果一个函数被编译成inline,原始的函数还是会被保留,我们可以直接在erlang shell中调用这个方法.

-module(test).

-compile(export_all).

-compile({inline,[server_id/0]}).


server_id() ->
2396.

内联编译不一定提高运行时的效率.例如内联可能增加Beam的栈消耗,对于递归函数调用这显然是有损性能的.{inline_size,Size}就是用来控制方法在多大程度上可以inline.Size默认值是24,这样inline代码与没有做inline的代码size相当,只有相当小的方法会被做inline.

那这个Size到底是指什么的大小呢?是代码函数?是代码个数?还是别的什么?我在erlangqa.org提了这个问题,得到了litaocheng的解答:

http://www.erlangqa.com/?qa=100/inline_size-size-%E4%B8%AD%E7%9A%84size%E6%98%AF%E6%8C%87%E4%BB%80%E4%B9%88

请参考otp_src/compiler/src/cerl_inline.erl的weight/1函数。
相应的Erlang表达式都有不同的权重。inline_size指的是函数汇编后的权重值。
可以通过 erlc +\'S\' your.erl来得到汇编文件:your.S。

同时参考cerl_inline.erl文件中:当inline_size为30时,90%的情况下可以得到最大加速。inline_size为100-150时,98%的情况下可以最大优化。如果指定更大值,会使代码尺寸变大,性能反而受到影响。

按图索骥找到cerl_inline.erl weight/1的代码:
weight Code
也就是说,不同的表达式有不同的权重值,Size既不是代码函数也不是函数个数,而是依赖于该权重值.这里我不再继续跟进了,cerl_inline.erl的注释提供了详细的信息:
Normal execution times for inlining are between 0.1 and 0.3 seconds (on the author's current equipment). The default effort limit of 150 is high enough that most normal programs never hit the limit even once, and for difficult programs, it generally keeps the execution times below 2-5 seconds. Using an effort counter of 1000 will thus have no further effect on most programs, but some programs may take as much as 10 seconds or more. Effort counts larger than 2500 have never been observed even on very ill-conditioned programs.

Size limits between 6 and 18 tend to actually shrink the code, because of the simplifications made possible by inlining. A limit of 16 seems to be optimal for this purpose, often shrinking the executable code by up to 10%. Size limits between 18 and 30 generally give the same code size as if no inlining was done (i.e., code duplication balances out the simplifications at these levels). A size limit between 1 and 5 tends to inline small functions and propagate constants, but does not cause much simplifications do be done, so the net effect will be a slight increase in code size. For size limits above 30, the executable code size tends to increase with about 10% per 100 units, with some variations depending on the sizes of functions in the source code.

Typically, about 90% of the maximum speedup achievable is already reached using a size limit of 30, and 98% is reached at limits around 100-150; there is rarely any point in letting the code size increase by more than 10-15%. If too large functions are inlined, cache effects will slow the program down.
感兴趣的可以找到原始论文看下,论文地址: "Fast and Effective Procedure Inlining", International Static Analysis Symposium 1997  http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.54.2438 
算法不跟进,但是实验要做的,就按照litaocheng建议的方法,我做了一个这样的demo,写一个简单的方法,这个方法在另一个方法里面被多次调用(代码如下).我是在windows环境中使用,在shell中使用命令c(test,['S']).得到assembler code文件.

-module(test).

-export([get_name/0, show/1]).


get_name() ->
"This is Test Module".


show(A) ->

A=get_name(),

A=get_name(),

A=get_name(),

A=get_name(),

A=get_name(),

A=get_name(),
io:format("This is ~p ~n",[A]).

生成的assembler code文件如下:

{module, test}. %% version = 0


{exports, [{get_name,0},{module_info,0},{module_info,1},{show,1}]}.


{attributes, []}.


{labels, 15}.



{function, get_name, 0, 2}.

{label,1}.

{line,[{location,"test.erl",22}]}.

{func_info,{atom,test},{atom,get_name},0}.

{label,2}.

{move,{literal,"This is Test Module"},{x,0}}.

return.



{function, show, 1, 4}.

{label,3}.

{line,[{location,"test.erl",25}]}.

{func_info,{atom,test},{atom,show},1}.

{label,4}.

{allocate,1,1}.

{move,{x,0},{y,0}}.

{line,[{location,"test.erl",26}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,5},[{x,0},{y,0}]}.

{line,[{location,"test.erl",27}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,6},[{x,0},{y,0}]}.

{line,[{location,"test.erl",28}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,7},[{x,0},{y,0}]}.

{line,[{location,"test.erl",29}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,8},[{x,0},{y,0}]}.

{line,[{location,"test.erl",30}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,9},[{x,0},{y,0}]}.

{line,[{location,"test.erl",31}]}.

{call,0,{f,2}}.

{test,is_eq_exact,{f,10},[{x,0},{y,0}]}.

{test_heap,2,0}.

{put_list,{y,0},nil,{x,1}}.

{move,{literal,"This is ~p ~n"},{x,0}}.

{line,[{location,"test.erl",32}]}.

{call_ext_last,2,{extfunc,io,format,2},1}.

{label,5}.

{line,[{location,"test.erl",26}]}.

{badmatch,{x,0}}.

{label,6}.

{line,[{location,"test.erl",27}]}.

{badmatch,{x,0}}.

{label,7}.

{line,[{location,"test.erl",28}]}.

{badmatch,{x,0}}.

{label,8}.

{line,[{location,"test.erl",29}]}.

{badmatch,{x,0}}.

{label,9}.

{line,[{location,"test.erl",30}]}.

{badmatch,{x,0}}.

{label,10}.

{line,[{location,"test.erl",31}]}.

{badmatch,{x,0}}.



{function, module_info, 0, 12}.

{label,11}.

{line,[]}.

{func_info,{atom,test},{atom,module_info},0}.

{label,12}.

{move,{atom,test},{x,0}}.

{line,[]}.

{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.



{function, module_info, 1, 14}.

{label,13}.

{line,[]}.

{func_info,{atom,test},{atom,module_info},1}.

{label,14}.

{move,{x,0},{x,1}}.

{move,{atom,test},{x,0}}.

{line,[]}.

{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

添加了inline选项之后的结果,这个demo比较变态生成的代码效果也比较明显:

{module, test}. %% version = 0


{exports, [{get_name,0},{module_info,0},{module_info,1},{show,1}]}.


{attributes, []}.


{labels, 10}.



{function, get_name, 0, 2}.

{label,1}.

{line,[{location,"test.erl",23}]}.

{func_info,{atom,test},{atom,get_name},0}.

{label,2}.

{move,{literal,"This is Test Module"},{x,0}}.

return.



{function, show, 1, 4}.

{label,3}.

{line,[{location,"test.erl",26}]}.

{func_info,{atom,test},{atom,show},1}.

{label,4}.

{test,is_eq_exact,{f,5},[{literal,"This is Test Module"},{x,0}]}.

{test_heap,2,1}.

{put_list,{x,0},nil,{x,1}}.

{move,{literal,"This is ~p ~n"},{x,0}}.

{line,[{location,"test.erl",33}]}.

{call_ext_only,2,{extfunc,io,format,2}}.

{label,5}.

{line,[{location,"test.erl",27}]}.

{badmatch,{literal,"This is Test Module"}}.



{function, module_info, 0, 7}.

{label,6}.

{line,[]}.

{func_info,{atom,test},{atom,module_info},0}.

{label,7}.

{move,{atom,test},{x,0}}.

{line,[]}.

{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.



{function, module_info, 1, 9}.

{label,8}.

{line,[]}.

{func_info,{atom,test},{atom,module_info},1}.

{label,9}.

{move,{x,0},{x,1}}.

{move,{atom,test},{x,0}}.

{line,[]}.

{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

官方文档: http://www.erlang.org/doc/man/compile.html
Erlang Assembly Code处于 not documented的状态,下面有两篇相关的文章:
Howto dump Core Erlang and assembly  http://untroubled.be/docs/erlang/howto_dump_core_erlang_and_assembly.html
How to modify Erlang assembly? Are any resources available?  http://stackoverflow.com/questions/7935054/how-to-modify-erlang-assembly-are-any-resources-available
 



推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文介绍了在使用Visual Studio 2015进行项目开发时,遇到类向导弹出“异常来自 HRESULT:0x8CE0000B”错误的解决方案。通过具体步骤和实践经验,帮助开发者快速排查并解决问题。 ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 本文介绍如何在 Android 中通过代码模拟用户的点击和滑动操作,包括参数说明、事件生成及处理逻辑。详细解析了视图(View)对象、坐标偏移量以及不同类型的滑动方式。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • PyCharm下载与安装指南
    本文详细介绍如何从官方渠道下载并安装PyCharm集成开发环境(IDE),涵盖Windows、macOS和Linux系统,同时提供详细的安装步骤及配置建议。 ... [详细]
  • 本文详细介绍了如何解决Uploadify插件在Internet Explorer(IE)9和10版本中遇到的点击失效及JQuery运行时错误问题。通过修改相关JavaScript代码,确保上传功能在不同浏览器环境中的一致性和稳定性。 ... [详细]
  • 导航栏样式练习:项目实例解析
    本文详细介绍了如何创建一个具有动态效果的导航栏,包括HTML、CSS和JavaScript代码的实现,并附有详细的说明和效果图。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文介绍了如何使用JQuery实现省市二级联动和表单验证。首先,通过change事件监听用户选择的省份,并动态加载对应的城市列表。其次,详细讲解了使用Validation插件进行表单验证的方法,包括内置规则、自定义规则及实时验证功能。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
author-avatar
mobiledu2402852357
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有