论坛首页 综合技术论坛

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

浏览 8053 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-16  
FP
引用
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)
   发表时间: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附值吗 ? 有点强迫症.
0 请登录后投票
   发表时间: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>

这个呢,能说上面的方法性能能好吗?
0 请登录后投票
   发表时间:2007-06-21  
引用
说说上面哪个性能好点.
第二个算lazy附值吗 ? 有点强迫症。

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

至于上文所说的闭包“可能”有的性能问题,我有理由认为,那是字符串连接的问题。你可以profile一下试试。被返回的函数事实上只创建了一次。
0 请登录后投票
   发表时间:2007-06-21  
Lich_Ray 写道

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

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


Lich_Ray 写道

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

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

这个环境的代价.

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


能给说说 javascript 中的 Monad 吗?
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics