热门标签 | 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
 



推荐阅读
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 三角测量计算三维坐标的代码_双目三维重建——层次化重建思考
    双目三维重建——层次化重建思考FesianXu2020.7.22atANTFINANCIALintern前言本文是笔者阅读[1]第10章内容的笔记,本文从宏观的角度阐 ... [详细]
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
  • OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战
    OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 本文探讨了如何通过编程手段在Linux系统中禁用硬件预取功能。基于Intel® Core™微架构的应用性能优化需求,文章详细介绍了相关配置方法和代码实现,旨在帮助开发人员有效控制硬件预取行为,提升应用程序的运行效率。 ... [详细]
  • 在PHP中,高效地分割字符串是一项常见的需求。本文探讨了多种技术,用于在特定字符(如“或”)后进行字符串分割。通过使用正则表达式和内置函数,可以实现更加灵活和高效的字符串处理。例如,可以使用 `preg_split` 函数来实现这一目标,该函数允许指定复杂的分隔符模式,从而提高代码的可读性和性能。此外,文章还介绍了如何优化分割操作以减少内存消耗和提高执行速度。 ... [详细]
  • 在Java基础中,私有静态内部类是一种常见的设计模式,主要用于防止外部类的直接调用或实例化。这种内部类仅服务于其所属的外部类,确保了代码的封装性和安全性。通过分析JDK源码,我们可以发现许多常用类中都包含了私有静态内部类,这些内部类虽然功能强大,但其复杂性往往让人感到困惑。本文将深入探讨私有静态内部类的作用、实现方式及其在实际开发中的应用,帮助读者更好地理解和使用这一重要的编程技巧。 ... [详细]
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社区 版权所有