• 【C++ Efficiency】lazy evaluation(缓式评估)的4种用途


    lazy evaluation(缓式评估)

    以某种方式撰写你的classes,使它们延缓运算,直到那些运算结果刻不容缓被迫切需要为止。

    lazy evaluation的4种用途

    1. Reference Counting(引用计数)

    class String{}; //string类
     
    String s1 = "Hello";
    String s2 = s1;
    
    • 1
    • 2
    • 3
    • 4

    上述这种通过复制赋值会引起较大的成本代价,s2以s1为初值通常涉及到new操作符分配堆内存,需要调用strcpy函数复制s1的数据到s2的内存,这是eager evaluation(急式评估):只因string的复制赋值函数被调用。然而,此时s2并不需要还未被使用,不需要被赋予实际内容。

    lazy evaluation做法:让s1和s2共享一个值

    数据共享的唯一危机在于其中某个字符串被修改时发生:

    s2.convertToUpperCase();
    //该语句的话就必须将s2内容做副本给s2私人使用了
    
    • 1
    • 2

    lazy evaluation:
    在真正需要之前,不用着急复制出副本,尽量拖延;
    只要可以就共享使用其它值。在某些领域,常有可能永远不用提供副本了。

    2. 区分读和写

    String s = "Homer's Iliad";   
    ...
    cout << s[3];			  //调用operator[] 读取s[3]
    s[3] = 'x';				  //调用operator[] 写入s[3]
    
    • 1
    • 2
    • 3
    • 4

    上述语句中,对引用而来的字符串进行读取操作是无需什么代价的,但是写入操作,可能需要先做出一个副本。

    困难在于:我们无法判断调用operator[]是读还是写。

    运用lazy evaluation和proxy class,我们可以延缓决定是读还是写操作,直到能做出判断。

    3. Lazy Fetching(缓式取出)

    //假设程序使用了包含许多对象的大型对象
    //为保持每次执行的一致性与连贯性,它们必须被存储于一个数据库里
    //每一个对象都有唯一的标识符,用来从数据库里取回对象
    
    class LargeObject
    {
    public:
    	LargeObject(ObjectID id);    //从磁盘中回存对象
    	const string& field1() const;
    	int field2() const;
    	double field3() const;
    	double string& field4() const;
    	double string& field5() const;
    	...
    };
    
    //从磁盘中回存对象的成本:
    void restoreAndProcessObject(ObjectID id)
    {
    	LargeObject object(id);
    	...
    }
    
    // LargeObject体积很大,获取所有的数据成本非常大,而且并非所有数据都必要
    void restoreAndProcessObject(ObjectID id)
    {
    	LargeObject object(id);
    	if(object.field2() == 0)
    		cout << "Object" << id << ":null field2.\n";
    }
    //其实仅需field2的值,所以获取其他字段的付出都是浪费
    
    • 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

    lazy evaluation做法:
    当需要某个数据的时候,才从数据库中去读取,即“demand-paged”(按需分页)对象初始化的实现方法。

    class LargeObject
    {
    public:
    	LargeObject(ObjectID id); 
    	const string& field1() const;
    	int field2() const;
    	double field3() const;
    	double string& field4() const;
    	double string& field5() const;
    	...
     
    private:
    	ObjectID oid;
    	
    	//mutable表示在任何函数里它们都能被修改,甚至const成员函数
    	mutable string* field1Value;   
    	mutable int* field2Value;
    	mutable double* field3Value;
    	mutable string* field4Valuel
    	...
    };
     
    LargeObject::LargeObject(ObjectID id)
    	:oid(id),field1Value(0),field2Value(0),field3Value(0)...
    	{}
    	
    const string& LargeObject::field1() const
    {
    	if(field1Value  == 0)
    	{
    		//从数据库中读取field1的值,使field1Value指向这个值
    	}
    	return *field1Value;
    }
    
    • 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
    • 对象中每个字段都用一个指向数据的指针来表示,LargeObject构造函数把每个指针初始化为null:null指针表示字段还没从数据库中读取数值;
    • 每个LargeObject成员函数在访问字段指针的所指向的数据之前必须检查字段指针的状态:如果为null,则对数据进行操作之前必须从数据库中读取对应的数据。

    4. Lazy Expression Evaluation(表达式缓评估)

    //数值应用
    template<class T>	
    class Matrix{...};			//homogeneous matrices(同质矩阵)
     
    Matrix<int> m1(1000,1000);	//一个1000*1000的矩阵
    Matrix<int> m2(1000,1000);
    ...
    Matrix<int> m3 = m1 + m2;	
    
    //+的实现是eagar evaluation:
    //计算和返回m1 与 m2的和:大规模运算且大量分配内存
    
    //lazy evaluation做法:
    //建立一个数据结构于m3中,表示m3的值是m1和m2的和
    Matrix<int> m4(1000,1000);
    Matrix<int> m3 = m4*m1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    lazy evaluation可以避免非必要的数值计算动作。

    总结

    上述四种用途展示了lazy evaluation在许多领域的作用:可避免非必要的对象复制,可区别operator[ ]的读取与写入操作,可避免非必要的数据库读取操作,可避免非必要的数值计算动作。

    但是,只有当“你的软件被要求执行某些计算,而那些计算其实可以避免”的情况下,lazy evaluation才有用处。

  • 相关阅读:
    arthas实用梳理篇
    4、nerf(pytorch)
    java 实习面经 —— 含大厂
    磁盘、内存和硬盘的区别
    STC 51单片机57——矩阵键盘 基本原理演示
    当服务器有可视化界面,直接起飞!
    网络规模与性能优化的一篇随笔
    【华为OD机试】仿 LISP 运算
    『亚马逊云科技产品测评』活动征文|利用EC2云服务器快速部署一个SpringBoot项目
    R语言使用lm函数构建多元回归模型(Multiple Linear Regression)、构建没有截距项的回归模型(模型不包含截距)
  • 原文地址:https://blog.csdn.net/weixin_49347928/article/details/133106230