C++ 之 over-eager evaluation 超前评估

C++之超急评估

over-eager evaluation vs.
eager evaluation vs. lazy evaluation

在前面已经提到了C++地懒惰求值:不要为你程序功能之外的任何事情付出任何代价。在你总是需要执行某种计算,但是该计算地结果并不总是被用到地时候,lazy evaluation 绝对可以提高你的程序的性能。但是当计算的结果总是被需要的时候,我们的 “未雨绸缪”却可以给程序的性能带来极大的提升。

PS: 至于eager evaluation便是 “走一步看一步”,如果当前需要这个结果,那么计算处该结果。无论后面是否会被用到。

over-eager evaluation

关于over-eager evaluation的用法,下面的几个简单并且常见的例子便是很好的解释:

STL 之 vector的增长方式

在STL中,vector的空间的增长方式是 eager evaluation的最好的例子,在当前空间的capacity满时,再添加元素时,我们分配的内存时当前内存的两倍,并不仅仅只是用来刚好装下当前的新元素。关于STL的vector的insert的源代码,便可以说明:

PS: 下述代码截自: http://blog.csdn.net/shoulinjun/article/details/23450597

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39
template<class T>  

void MyVector<T>::insert(iterator position, size_type n, const T& value)  

{  

if(n <= end_of_storage - finish)  

{/* enough memory */   

if(n <= finish - position)  

{  

std::uninitialized_copy(finish-n, finish, finish);  

std::copy(position, finish-n, position+n);  

std::fill_n(position, n, value);  

}  

else  

{   

std::uninitialized_fill_n(finish, n - (finish - position), value);  

std::uninitialized_copy(position, finish, position + n);  

std::fill(position, finish, value);  

}  

finish += n;  

}  

else  

{/* reallocate */  

pointer new_start(NULL), new_finish(NULL);  

size_type old_type = end_of_storage - start;   

size_type new_size = old_type + std::max(old_type, n);  

new_start = alloc.allocate(new_size);  

// copy old vector to new vector  

new_finish = std::uninitialized_copy(start, position, new_start);  

std::uninitialized_fill_n(new_finish, n, value);  

new_finish += n;  

new_finish = std::uninitialized_copy(position, finish, new_finish);  

alloc.deallocate(start, end_of_storage - start);  

start = new_start;  

finish = new_finish;  

end_of_storage = new_start + new_size;  

}  

}

Caching 技术

在读取磁盘上的文件时,我们可以预先读取一些放在内存,降低下一次读取到该区域的开销,根据程序执行的 “局部性原理”。

分期摊还,就是降低单次操作的时间复杂度。

维护流式数据的 min max avg 值:

维护一个数据结构保存当前已有数据的最大值或最小值或平均值,降低单次操作的时间复杂度(严格的讲,这里的时间复杂度指的是平均时间复杂度)。

以时间换空间的例子。

比如,要求维护一个返回最小值的栈,这就是典型的使用额外的数据结构来维护最小值,使得返回最小值的时间复杂度为O(1)。代码为 :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55
class Stack{

private:

stack<int> elemStack;

stack<int> minStack;

int minElem;

public:

Stack(){minElem=INT_MAX;}

int getMin();

void Push(int val);

void Pop();

};

int Stack::getMin()

{

if (minStack.empty())

{

cout << "Stack is empty..." << endl;

return INT_MIN;

}

else

{

return minStack.top();

}

}

void Stack::Push(int val)

{

if (elemStack.empty())

{

elemStack.push(val);

minStack.push(val);

}

else

{

elemStack.push(val);

if (val < minStack.top())

{

minStack.push(val);

}

else

{

minStack.push(minStack.top());

}

}

}

void Stack::Pop()

{

if (elemStack.empty())

{

cout << "Stack is empty..." << endl;

return;

}

elemStack.pop();

minStack.pop();

}

Final words:

  1. 如果你预期程序常常需要某个运算,那么你可以“未雨绸缪”,使用数据结构(如上面的Cache以及min stack)降低该操作的单次时间复杂度。从而提高整个程序的运行效率。
  2. 这里的over-eager evaluation与lazy evaluation并不矛盾,前者是为某些一定会做的操作未雨绸缪;后者时绝不为不必要的操作付出额外的代价。
时间: 2024-11-06 08:20:50

C++ 之 over-eager evaluation 超前评估的相关文章

C++的拖延战术:lazy evaluation

在C++中这里的拖延战术拥有一个非常优雅的名字 -- Lazy evalution.一旦你的程序中使用了lazy evaluation,那么你就可以在你实际需要某些动作时编写相应的代码,如果不需要,那么相应的动作也就永远都不会执行. 那么我们在什么时候会用的上这样的技术呢? Reference Counting 引用计数 对于引用技术,相信大部分人都不觉得陌生,在C++中的智能指针shared_ptr便是利用这一技术的最佳人选.下面要讲的是C++的string类的实现,string类的实现(可能

PLT:说说Evaluation strategy

Brief 在学习方法/函数时,我们总会接触到 按值传值 和 引用传值 两个概念.像C#是按值传值,但参数列表添加了ref/out后则是引用传值,但奇怪的事出现了 namespace Foo{ class Bar{ public String Msg{get;set;} } class Program{ public static void main(String[] args){ Bar bar1 = new Bar(); bar1.Msg = "Hey, man!"; Update

More Effective C++----(17)考虑使用lazy evaluation(懒惰计算法)

Item M17:考虑使用lazy evaluation(懒惰计算法) 从效率的观点来看,最佳的计算就是根本不计算,那好,不过如果你根本就不用进行计算的话,为什么还在程序开始处加入代码进行计算呢?并且如果你不需要进行计算,那么如何必须执行这些代码呢? 关键是要懒惰. 还记得么?当你还是一个孩子时,你的父母叫你整理房间.你如果象我一样,就会说"好的",然后继续做你自己的事情.你不会去整理自己的房间.在你心里整理房间被排在了最后的位置,实际上直到你听见父母下到门厅来查看你的房间是否已被整理

MoreEffectiveC++Item35(效率)(条款16-24)

条款16 谨记80-20法则 80-20 准则说的是大约 20%的代码使用了 80%的程序资源:大约 20%的代码耗用了大约 80%的运行时间:大约 20%的代码使用了 80%的内存:大约 20%的代码执行 80%的磁盘访问:80%的维护投入于大约 20%的代码上:通过无数台机器.操作系统和应用程序上的实验这条准则已经被再三地验证过.80-20 准则不只是一条好记的惯用语,它更是一条有关系统性能的指导方针,它有着广泛的适用性和坚实的实验基础 条款17 考虑使用 lazy evaluation(缓

More Effective C++

条款一:指针与引用的区别 指针与引用看上去完全不同(指针用操作符'*'和'->',引用使用操作符'.'),但是它们似乎有相同的功能.指针与引用都是让你间接引用其他对象.你如何决定在什么时候使用指针,在什么时候使用引用呢? 首先,要认识到在任何情况下都不能用指向空值的引用.一个引用必须总是指向某些对象.因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量.相反,如果变量肯定指向一个对象,例如你的设计不允许变量为

More Effective C++读书小记

1.仔细区别pointer和references 不论pointer或是references都使你间接参考其它对象. 没有所谓的null reference.一个reference必须总代表某个对象. 如果你有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表)任何对象,那么你应该使用pointer,因为你可以将指针设为null.换个角度,如果这个变量总是代表一个对象,也就是说你的设计并不允许这个变量为null,那么你应该使用reference. 由于reference一定

即将推出的Apache Spark 2.4有什么新功能

本文来自于2018年09月19日在 Adobe Systems Inc 举行的 Apache Spark Meetup. 即将发布的 Apache Spark 2.4 版本是 2.x 系列的第五个版本. 本文对 Apache Spark 2.4 的主要功能和增强功能进行了概述. 新的调度模型(Barrier Scheduling),使用户能够将分布式深度学习训练恰当地嵌入到 Spark 的 stage 中,以简化分布式训练工作流程. 添加了35个高阶函数,用于在 Spark SQL 中操作数组/

MFC BCGCBProInc.h : No such s file or dictionary. 解决方案:安装BGB界面库

BCGCBProInc.h : No such s file or dictionary. 解决方案:安装BGB界面库 一.   关于BCGControlBar. BCGControlBar是一个基于MFC的扩展库,您可以通过完全的用户化操作构成一些类似于Microsoft Office 2000/XP/2003和Microsoft Visual Studio.NET的应用程序(用户工具栏.菜单.键盘等等).BCGControlBar库包含了大约150多个经过精心设计,测试和具有完备文档的MFC

俄罗斯方块

俄罗斯方块游戏自动机 <用electron制作俄罗斯方块游戏> 后续文章,智能程序玩俄罗斯方块游戏. 背景 前不久用ES6完成了基本的俄罗斯方块游戏,今天已经完成了一个初步的智能算法,可以自动玩俄罗斯方块了,让自己的想法朝实现更近了一步. 效果图 第一次运行,消除了1398行,窃喜! 程序结构 主要关注智能算法,结构简单化,全部放在了index.js中. 用定时器驱动游戏 function autoPlayClick(){ isAutoPlay = document.getElementByI