转载:【原译】Erlang性能的八个误区(Efficiency Guide)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 转自:http://www.cnblogs.com/futuredo/archive/2012/10/16/2725770.html   The Eight Myths of Erlang Performance Erlang/OTP R15B02 1  Myth: Funs are slo...

转自:http://www.cnblogs.com/futuredo/archive/2012/10/16/2725770.html

 

The Eight Myths of Erlang Performance

Erlang/OTP R15B02

1  Myth: Funs are slow

  Fun函数很慢(这里应该是指Module:Function(Arguments)这种形式的函数,其中M,F,A可以是变量类型,值不是固定的)

  Yes, funs used to be slow. Very slow. Slower than apply/3. Originally, funs were implemented using nothing more than compiler trickery, ordinary tuples, apply/3, and a great deal of ingenuity.

  是的,fun函数曾经很慢,非常慢,比apply/3方式还慢。最开始,fun函数只是通过编译器策略,普通元组,apply/3方式和其他一些新的东西来实现的。

  But that is ancient history. Funs was given its own data type in the R6B release and was further optimized in the R7B release. Now the cost for a fun call falls roughly between the cost for a call to local function and apply/3.

  但是那已经是过去的历史,在R6B版本中,fun函数有了自己的数据结构,在R7B版本中,fun函数进一步做了优化。现在一个fun函数调用的耗费大概在本地函数调用和apply/3方式之间。

2  Myth: List comprehensions are slow

  列表解析很慢

  List comprehensions used to be implemented using funs, and in the bad old days funs were really slow.

  列表解析以前是使用函数来实现的,而在糟糕的以前,函数非常慢。

  Nowadays the compiler rewrites list comprehensions into an ordinary recursive function. Of course, using a tail-recursive function with a reverse at the end would be still faster. Or would it? That leads us to the next myth.

  现在,编译器把列表解析重新写成一个普通的递归函数。当然,使用一个尾递归函数并且在最后反转效率将更快。是吗?让我们来看看下一个因素

3  Myth: Tail-recursive functions are MUCH faster than recursive functions

  尾递归函数比普通递归函数快得多

  According to the myth, recursive functions leave references to dead terms on the stack and the garbage collector will have to copy all those dead terms, while tail-recursive functions immediately discard those terms.

  谈到这个误区,递归函数把对不需要的数据引用保留在了堆栈上,垃圾回收器将必须拷贝所有这些不需要的数据,相反,尾递归函数会立即抛弃这些不需要的数据。

  That used to be true before R7B. In R7B, the compiler started to generate code that overwrites references to terms that will never be used with an empty list, so that the garbage collector would not keep dead values any longer than necessary.

  那种情况在R7B版本之前是对的,在R7B版本中,编译器开始通过使用一个空列表覆写那些将不会被用到的数据引用来产生代码,所以垃圾回收器在没必要的时候就可以不保留那些不需要的值。

  Even after that optimization, a tail-recursive function would still most of the time be faster than a body-recursive function. Why?

  即使是在优化过后,一个尾递归函数在大多数时候还是比一个体递归要快速,为什么?

  It has to do with how many words of stack that are used in each recursive call. In most cases, a recursive function would use more words on the stack for each recursion than the number of words a tail-recursive would allocate on the heap. Since more memory is used, the garbage collector will be invoked more frequently, and it will have more work traversing the stack.

  这个跟每次递归调用中有多少个字的堆栈空间被使用有关。在大多数情况下,一个普通递归调用每次占用堆栈空间的大小比尾递归要多。由于更多的内存被使用,垃圾回收器将被更频繁地唤醒,并花费更多的工作来遍历整个堆栈。

  In R12B and later releases, there is an optimization that will in many cases reduces the number of words used on the stack in body-recursive calls, so that a body-recursive list function and tail-recursive function that calls lists:reverse/1 at the end will use exactly the same amount of memory. lists:map/2lists:filter/2, list comprehensions, and many other recursive functions now use the same amount of space as their tail-recursive equivalents.

  在R12B及 以后的版本中,做了一个优化,使得在许多情况下能减小体递归调用时在堆栈上占用的空间,所以一个体递归函数和一个尾递归函数,同样在最后调用 lists:reverse/1函数实现列表反转,所使用的内存空间几乎相等。现在,lists:map/2,lists:filter/2,列表解析, 以及许多其他普通递归函数占用的内存空间跟它们的尾递归实现一样。

  So which is faster?

  那哪一个更快?

  It depends. On Solaris/Sparc, the body-recursive function seems to be slightly faster, even for lists with very many elements. On the x86 architecture, tail-recursion was up to about 30 percent faster.

  根据情况有所不同。在Solaris/Sparc上,体递归函数看起来稍微快一点,即使对于那些有很多元素的列表。在x86架构上,尾递归要比体递归快近30%。

  So the choice is now mostly a matter of taste. If you really do need the utmost speed, you must measure. You can no longer be absolutely sure that the tail-recursive list function will be the fastest in all circumstances.

  所以现在怎么选择只是一个喜好的问题。若果真的需要极快的速度,你必须衡量一下。你不再能保证在所有的情况下,尾递归列表函数是最快的。

  Note: A tail-recursive function that does not need to reverse the list at the end is, of course, faster than a body-recursive function, as are tail-recursive functions that do not construct any terms at all (for instance, a function that sums all integers in a list).

  注意:一个不需要在最后反转列表的尾递归函数,当然比一个体递归函数快,尾递归函数不构造任何数据项(例如,一个计算列表中所有整数的和的函数)

4  Myth: '++' is always bad

  '++'总是不好的

  The ++ operator has, somewhat undeservedly, got a very bad reputation. It probably has something to do with code like

  ++操作符有一个不好的声誉,某种程度上不应该是这样的。这个可能跟下面的代码有关

DO NOT

naive_reverse([H|T]) ->
    naive_reverse(T)++[H];
naive_reverse([]) ->
    [].

  which is the most inefficient way there is to reverse a list. Since the ++ operator copies its left operand, the result will be copied again and again and again... leading to quadratic complexity.

  这是反转一个列表最低效率的方式。由于++操作符复制它左边的操作数,得到的结果会被一遍又一遍的复制......导致二次方的复杂度

  On the other hand, using ++ like this

  另一方面,像这样使用++操作符还好

OK

naive_but_ok_reverse([H|T], Acc) ->
    naive_but_ok_reverse(T, [H]++Acc);
naive_but_ok_reverse([], Acc) ->
    Acc.

is not bad. Each list element will only be copied once. The growing result Acc is the right operand for the ++ operator, and it will not be copied.

  每个列表元素将仅被复制一次。增长中的结果是++操作符的右操作数,不会被复制。

  Of course, experienced Erlang programmers would actually write

  当然,有经验的Erlang程序员实际上会这样做

DO

vanilla_reverse([H|T], Acc) ->
    vanilla_reverse(T, [H|Acc]);
vanilla_reverse([], Acc) ->
    Acc.

  which is slightly more efficient because you don't build a list element only to directly copy it. (Or it would be more efficient if the the compiler did not automatically rewrite [H]++Acc to [H|Acc].)

  这个略微更有效率,因为你不再构建一个列表元素,只是直接复制它而已。(如果编译器不自动将[H]++Acc重写为[H|Acc],那上面的写法会更有效)

5  Myth: Strings are slow

  字符串很慢

  Actually, string handling could be slow if done improperly. In Erlang, you'll have to think a little more about how the strings are used and choose an appropriate representation and use the re module instead of the obsolete regexp module if you are going to use regular expressions.

  实际上,字符串处理如果做得不适当会显得很慢。在Erlang中,你将不得不稍稍多思考一下字符串是怎样被使用的,并选择一个恰当的展现方式,如果你要用正则表达式,使用re模块,不要用旧的regexp模块。

6  Myth: Repairing a Dets file is very slow

  修复一个Dets文件非常慢

  The repair time is still proportional to the number of records in the file, but Dets repairs used to be much, much slower in the past. Dets has been massively rewritten and improved.

  现在的修复时间仍然和文件里记录的数目成正比,但是Dets文件的修复在以前要慢很多很多。Dets已经被大量地做了重写和改进。

7  Myth: BEAM is a stack-based byte-code virtual machine (and therefore slow)

  BEAM是一个基于堆栈的字节码虚拟机(因此很慢)

  BEAM is a register-based virtual machine. It has 1024 virtual registers that are used for holding temporary values and for passing arguments when calling functions. Variables that need to survive a function call are saved to the stack.

  BEAM是一个基于寄存器的虚拟机。它有1024个用来存储临时变量和在调用函数时传参的寄存器。在函数调用期间需要存在的变量被保存在堆栈中

  BEAM is a threaded-code interpreter. Each instruction is word pointing directly to executable C-code, making instruction dispatching very fast.

  BEAM是一个threaded-code(螺纹代码?)解释器。每条指令直接指向可执行C代码,使得指令调度非常快

8  Myth: Use '_' to speed up your program when a variable is not used

  用'_'来表示不使用的变量来加快你的程序

  That was once true, but since R6B the BEAM compiler is quite capable of seeing itself that a variable is not used.

  这个以往是正确的,但是自R6B版本以来,BEAM编译器完全有能力注意到有个变量未被使用。

相关文章
|
4月前
|
Java 编译器 测试技术
深入浅出 Compose Compiler(5) 类型稳定性 Stability
深入浅出 Compose Compiler(5) 类型稳定性 Stability
69 0
深入浅出 Compose Compiler(5) 类型稳定性 Stability
|
JavaScript 开发者
怎么用 Performance 工具查看任务
怎么用 Performance 工具查看任务
140 0
怎么用 Performance 工具查看任务
|
存储 Kubernetes Cloud Native
云原生渐进式交付,刷 Argo CD 技术文档之 Understand The Basics & Core Concepts 篇
云原生渐进式交付,刷 Argo CD 技术文档之 Understand The Basics & Core Concepts 篇
134 0
|
Java 编译器
An Introduction to JWarmup
一、JWarmup背景 二、JWarmup功能 三、案例演示
An Introduction to JWarmup
|
SQL 存储 数据采集
【详谈 Delta Lake 】系列技术专题 之 基础和性能(Fundamentals and Performance)
本文翻译自大数据技术公司 Databricks 针对数据湖 Delta Lake 的系列技术文章。众所周知,Databricks 主导着开源大数据社区 Apache Spark、Delta Lake 以及 ML Flow 等众多热门技术,而 Delta Lake 作为数据湖核心存储引擎方案给企业带来诸多的优势。本系列技术文章,将详细展开介绍 Delta Lake。
【详谈 Delta Lake 】系列技术专题 之 基础和性能(Fundamentals and Performance)
|
网络架构 Java Go
带你读《计算机体系结构:量化研究方法(英文版·原书第6版)》之一:Fundamentals of Quantitative Design and Analysis
本书堪称计算机系统结构学科的“圣经”,是计算机设计领域学生和实践者的必读经典。本书系统地介绍了计算机系统的设计基础、存储器层次结构设计、指令级并行及其开发、数据级并行、GPU体系结构、线程级并行和仓库级计算机等。本书内容丰富,既介绍了当今计算机体系结构的研究成果,也引述了许多计算机系统设计开发方面的实践经验。另外,各章结尾还附有大量的习题和参考文献。
|
机器学习/深度学习 人工智能 算法
[python作业AI毕业设计博客]Analytic Methods in Systems and Software Testing-2018 系统和软件测试分析方法
图片.png 下载地址 https://itbooks.pipipan.com/fs/18113597-335471247 使用最先进的方法和工具对系统和软件测试进行综合处理。本书提供了有关最新软件测试方法的宝贵见解,并通过示例解释了该领域中使用的统计和分析方法。
|
内存技术 Go Windows
带你读《计算机组成与体系结构:性能设计(英文版·原书第10版)》之一:Basic Concepts and Computer Evolution
本书以Intel x86体系结构和ARM两个处理器系列为例,将当代计算机系统性能设计问题与计算机组成的基本概念和原理紧密联系起来,介绍了当代计算机体系结构的主流技术和最新技术。本书作者曾13次获a得美国教材和学术专著作者协会颁发的年度最佳计算机科学教材奖。目前,他是一名独立顾问,为众多计算机和网络制造商、软件开发公司以及政府前沿研究机构提供服务。
|
图形学 内存技术 Java
带你读《计算机组成与体系结构:性能设计(英文版·原书第10版)》之二:Performance Issues
本书以Intel x86体系结构和ARM两个处理器系列为例,将当代计算机系统性能设计问题与计算机组成的基本概念和原理紧密联系起来,介绍了当代计算机体系结构的主流技术和最新技术。本书作者曾13次获a得美国教材和学术专著作者协会颁发的年度最佳计算机科学教材奖。目前,他是一名独立顾问,为众多计算机和网络制造商、软件开发公司以及政府前沿研究机构提供服务。
|
存储
分布式系统的烦恼------《Designing Data-Intensive Applications》读书笔记11
使用分布式系统与在单机系统中处理问题有很大的区别,分布式系统带来了更大的处理能力和存储容量之后,也带来了很多新的"烦恼"。在这一篇之中,我们将看看分布式系统带给我们新的挑战。
1275 0