`
Lich_Ray
  • 浏览: 163387 次
  • 性别: Icon_minigender_1
  • 来自: 南京
文章分类
社区版块
存档分类
最新评论

函数式语言:我的性能没问题

阅读更多
引用
lichray 将用几天的时间写完本文系列文章的全部,剩下的文章将会发布在新建的 函数式编程の道 圈子上。这些文章将并非是从编译原理的角度来探讨函数式编程语言的文章。本文只会浅尝辄止地覆盖函数式编程语言的编译、解释优化手段,并试图让大家相信:使用函数式编程语言/风格,获得的只是表达能力上的大幅提升,而程序性能几乎不会下降。
PS: 文章中部分内容可以在《现代编译原理》这本书中找到。


一. 内存分配优化

1. 高阶函数
* 针对不纯的函数式编程语言:
不纯的函数式编程语言其实也就是比普通的命令式语言多了这个高阶函数罢了,但性能会造成下降:因为函数必须能具有普通变量的可操作性,比如作参数、返回值,函数必须作为数据的一种、被分配在堆上而不是栈上。
在处理在堆上分配函数方面,函数的创建、查找对于现在的算法来说已不是问题,关键是处理重复的函数——数据重复一般是无所谓,因为创建速度太快,而函数就不同了;如果在一个循环中创建函数,那岂不是要了函数式编程语言的命?
for (var i = 0; i < 10; i++) {
	var f = function (a) {
		return a * a
	}
	print(f(i))
}

对于“相同的函数”,有必要只保留一个。
如果两个函数是“相同”的,需要满足下面两个定义中的一个:
  • 二者从程序的源代码文本的同一处获取其函数体
  • 在使用 eval() 函数从产生程序时,二者从其参数的同一处获取其函数体

这样简单的定义已经能够处理大部分情况了(比如上面那个例程)。

* 针对纯函数式编程语言:
纯函数式编程语言的要求更加严格,决不允许出现赋值,但优化起来可以更“放心”。例如,如果编译/解释该语言需要用到中间代码,下面的优化就可以办到:
  • 进入执行上下文,重命名内部绑定的变量,用中间代码翻译所有函数体,若存在中间代码相同的函数,认为它们是同一个函数
  • 在使用 eval() 函数从产生程序时……(略)

这样一来,重复说明函数体相同(或只有内部绑定的变量命名不同)的函数将被优化——因为变量的绑定不会改变,不用担心调用两个函数造成的“结果”不同。

2. 词法作用域
对于支持词法作用域(套嵌函数)的语言来说,因为函数拥有有执行上下文,函数还必须“记住”自己是在哪儿被调用的,在那儿还声明了什么变量,因此需要一个“环境”,雪上加霜。
一些书上会这样描述一个词法作用域查找变量绑定位置的过程:
  1. 查找本级作用域是否包含该变量的绑定
  2. 如果 Result(1) 为假,转到 Step4
  3. 返回本级作用域中该变量的绑定
  4. 如果本级作用域是全局作用域(顶层),报错
  5. 对上级(调用环境)作用域应用 Step1

言下之意,函数都是被保存在一个链表中的指针所指向的,然后就像访问链表那样查找本级、上级……然后算法复杂度为 O(n)。但你觉得这可能吗?看看下面的 Scheme 代码:
(define (fact n)
	(if (= n 0)
		1
		(* n (fact (- n 1)))))

完了,要创建深度为 n 的链表啦,回忆一下 C 里面的 malloc() 的效率,一般 profile 出来的罪魁祸首就是它,不觉得毛骨悚然吗?
没有一个真正的函数式编程语言实现是在链表上分配函数的,函数被分配在哈希表中的可能性其实也不大(因为缺乏层次搜索能力),一般都是在经过特殊优化的二叉树上分配。真正的变量搜索算法也不可能那么傻,一般只是把原有的符号表“立体化”。此外,通过先进的活跃性分析技术,更有可能被重复使用的函数所需的逃逸变量将更容易被找到。

(To be continued)
分享到:
评论
4 楼 radar 2007-06-21  
Lich_Ray 写道

这个不能算是 lazy 赋值吧。在普通的 JavaScript 中可以使用 lazy 的思想但不能使用 lazy 的写法(也就是说,强迫症。不过也可以不这么挑剔,把它看成是 JavaScript 的强大)。想在 JS 里用上“写法上的”惰性运算需要 mozilla 的C版本 JS 解释器支持。

写法上的好说. 但如果不是真正的lazy 附值,那javascript 最好别放到 函数式语言 范畴.


Lich_Ray 写道

至于上文所说的闭包“可能”有的性能问题,我有理由认为,那是字符串连接的问题。你可以profile一下试试。被返回的函数事实上只创建了一次。

2. 词法作用域
创建几次不是关键,是执行过程中,
引用
因为函数拥有有执行上下文,函数还必须“记住”自己是在哪儿被调用的,在那儿还声明了什么变量,因此需要一个“环境”,雪上加霜。

这个环境的代价.

我没否定的意思,而是有些困惑.


能给说说 javascript 中的 Monad 吗?
3 楼 Lich_Ray 2007-06-21  
引用
说说上面哪个性能好点.
第二个算lazy附值吗 ? 有点强迫症。

这个不能算是 lazy 赋值吧。在普通的 JavaScript 中可以使用 lazy 的思想但不能使用 lazy 的写法(也就是说,强迫症。不过也可以不这么挑剔,把它看成是 JavaScript 的强大)。想在 JS 里用上“写法上的”惰性运算需要 mozilla 的C版本 JS 解释器支持。

至于上文所说的闭包“可能”有的性能问题,我有理由认为,那是字符串连接的问题。你可以profile一下试试。被返回的函数事实上只创建了一次。
2 楼 radar 2007-06-20  
引用
2. 词法作用域

我试着用闭包理解下,感觉闭包和你描述的感觉挺象的.
javascript中的 Execution Context   性能感觉是问题.
<SCRIPT LANGUAGE="JavaScript">
<!--
var DivTemplete=(function(){
	var templete=[
		'<div id="',
		'',   //index 1, DIV ID attribute
		'" style="position:absolute;top:',
		'',   //index 3, DIV top position
		'px;left:',
		'',   //index 5, DIV left position
        'px;width:',
		'',   //index 7, DIV width
		'px;height:',
		'',   //index 9, DIV height
		'\"><\/div>'
	];
	return (function(id,top,left,width,height){
		templete[1] = id;
		templete[3] = top;
		templete[5] = left;
		templete[7] = width;
		templete[9] = height;
		return templete.join('');
    })
})();
alert(DivTemplete('11',12,23,34,45))
//-->
</SCRIPT>

这样写可能性能没影响.
<SCRIPT LANGUAGE="JavaScript">
<!--
function outer(arg1, arg2){
    var localVar = 8;
    function inner(innerArg){
        return ((arg1 + arg2)/(innerArg + localVar));
    }
    return inner;
}
var globalVar = outer(2, 4);
alert(globalVar(2))
//-->
</SCRIPT>

这个呢,能说上面的方法性能能好吗?
1 楼 radar 2007-06-20  
<SCRIPT LANGUAGE="JavaScript">
<!--
function f(p){
    return p+1;
}
function g(p){
	return p+2;
}
alert(g(f(5)));
/**
g(f(5))
var temp=f(5);
g(temp)
*/
//-->
function gg(p){
	function ff(p){
        return p+1;
	}
	return ff(p+2);
}
alert(gg(5));
/**
  闭包包,是 lazy附值吗 ????
*/
</SCRIPT>

先让唵搞清楚高阶函数.
说说上面哪个性能好点.
第二个算lazy附值吗 ? 有点强迫症.

相关推荐

    轻量级Web服务器teepeedee2.zip

    John Fremlin去年在东京的Linux用户组上演示了teepeedee2的性能,他声称函数式编程语言(LISP属于函数语言)能战胜C语言。现在他的博客运行的就是teepeedee2,能即时显示用户评论,被/.之后也没出现问题。 ...

    java面试题

    答:折构函数式销毁一个类的函数,虚函数是为了C++的动态绑定而设计的。 描述你的编程风格? 答:类名首字母大写,常量一般全部大写,给自己的代码加注释。 控制流程? 答:控制流程一般使用if判断条件。有第二分支...

    基于AT89S52 单片的频率计

    率值就越准确,但闸门时间越长则没测一次频率的间隔就越长。闸门时间越 短,测的频率值刷新就越快,但测得的频率精度就受影响本文。数字频率计是 用数字显示被测信号频率的仪器,被测信号可以是正弦波,方波或其它...

    C#微软培训资料

    10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法的声明.124 11.2 方法中的参数.125 11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 ...

    超级有影响力霸气的Java面试题大全文档

    多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 5、String是最基本的数据类型吗?  基本数据类型包括byte、int、char、long、float、double、boolean和short。  java....

    Tcl_TK编程权威指南pdf

    这本书就是一本帮助你最大限度地利用Tcl/Tk并回避一些我所经历过的令人头痛的问题的实用编程指南。 我接触Tcl语言大概已经有10年的时间了,而本书的第一版也已经出版5年了。在过去的几年中,我一直在John ...

    GoodProject Maven Webapp.zip

    jQuery EasyUI是基于JQuery的一个前台ui界面的插件,功能相对没extjs强大,但页面也是相当好看的,同时页面支持各种themes以满足使用者对于页面不同风格的喜好。一些功能也足够开发者使用,相对于extjs更轻量。 ...

    算法导论(part1)

    有一点可能是让人难以置信的,就是在本书这样一本大部头中,由于篇幅的原因,很多有趣的算法都没能包括进来。.. 尽管学生们发来了大量的请求,希望我们提供思考题和练习的解答,但我们还是决定不提供思考题和练习的...

    java 面试题 总结

    多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang....

    windows 程序设计

    (本章后面我会谈到一些寻址空间的问题)。Windows NT还可以移植到非Intel处理器上,并在几种使用RISC芯片的工作站上执行。 Windows 95是在1995年8月发布的。和Windows NT一样,Windows 95也支持Intel 386或更高等级...

    算法导论(part2)

    有一点可能是让人难以置信的,就是在本书这样一本大部头中,由于篇幅的原因,很多有趣的算法都没能包括进来。.. 尽管学生们发来了大量的请求,希望我们提供思考题和练习的解答,但我们还是决定不提供思考题和练习的...

    课程设计简易计算器设计

    17世纪初,西方国家的计算工具有了较大的发展,英国数学家纳皮尔发明的"纳皮尔算筹",英国牧师奥却德发明了圆柱型对数计算尺,这种计算尺不仅能做加减乘除、乘方、开方运算,甚至可以计算三角函数,指数函数和对数...

    二十三种设计模式【PDF版】

    提供 Java运行性能,降低小而大量重复的类的开销. C. 行为模式 设计模式之 Command(命令) 什么是将行为封装,Command 是最好的说明. 设计模式之 Observer(观察者) 介绍如何使用 Java API 提供的现成 Observer ...

    Linux操作系统基础教程

    第三讲 Linux下的网络服务,配置问题和常用工具.................................................................24 一.Linux下的网络服务.....................................................................

    ABFrameWork Help.chm

    (3)、解决简繁体与多语言的问题且可运行时动态切换 在简体下编译的程序不用任何修改,就可以在繁体下运行且显示繁体,反之也然,可动态增加、删除或切换语言。 (4)、先进的EXE+BPL分发架构 框架采用EXE +BPL...

Global site tag (gtag.js) - Google Analytics