copy-on-write学习

最近知识梳理不够,那就整理点之前blog的东西。
这儿就看COW(copy-on-write),cow技术主要是为了提升程序在单步操做时的系统响应速度而设计的,
它经过将不是当即必要的空间分配,数据复制等耗时操做分摊到后续的某个步骤中,以部分提高性
能。但这种瞬时的性能提高,一般是以部分牺牲整体性能为代价的。linux

1. copy-on-write的优势
(1)cow可以减小单步操做时因为分配空间及数据复制带来的瞬间延迟
(2)cow可以在必定程度上启动空间优化的做用c++

2. copy-on-write的应用
(1)cow在g++ std:string中的使用
对于string的实现,不一样的程序库有不一样的实现方法,g++版的std:string采用的cow技术,
SGI STL string则是采用的eager copy,而Visual c++ 2010中string则是采用的sso,此两
种方法会在附录有简单介绍。git

在下面的代码中,证实了g++ std:string中对cow技术的使用:github

#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
string s_str_1 = "hello ymc!";
string s_str_2 = s_str_1;

printf("Begin:\n");
printf("s_str_1 At:%x\ns_str_2 At:%x\n", s_str_1.c_str(), s_str_2.c_str());

s_str_2[0] = 'y';

printf("\nAfter s_str_2 are modified:\n");
printf("s_str_1 At:%x\ns_str_2 At:%x\n", s_str_1.c_str(), s_str_2.c_str());

return 0;
}

以上的代码的运行结果为:安全

Begin:
s_str_1 At:95a2014
s_str_2 At:95a2014

After s_str_2 are modified:
s_str_1 At:95a2014
s_str_2 At:95a2034

 

可见,在首次进行赋值时,g++ std:string使用了cow.下面看cow的具体实现。
cow实际是使用计数的方法,使数据在有不一样虚拟地址空间的同时,有着相同的物理存储位置,而
具体的的空间分配数据复制则被延迟到数据有个性化需求时。
下面的代码给出了部分cow的实现:多线程

class string
{
public:
string()
:_data(new char[1])
{
*_data = '\0';
}

string(const char* t_str)
{
if(_data != NULL)
delete _data;
_size = strlen(t_str);
_data = new char[_size + 2];
memset(_data, 0, _size+2);
_data++;
strcpy(_data, t_str);
}
//copy-on-write
string(const string& t_str)
{
if(str != *this)
{
_data = str._data;
_size = str._size; 
_data[-1]++;
}
}
//copy when writing case 1:
char& operator[](unsigned int t_index)
{
if(t_index > _size || _data == NULL)
{
return NULL;
}

char* c_tmp = new char[_size + 2];
memset(c_tmp, 0, _size+2);
strcpy(c_tmp+1, _data);
_data[-1]--;
_data = c_tmp;

return _data[t_index];
}

private:
char* _data;
uint32_t _size; 
}

 


上面的实现中仅仅实现拷贝构造函数及[]操做符部分。一些细节也未在考虑以内,仅专一了cow方面。
一个完整的cow的string的实现还须要增长赋值运算符,+=运算及其它一些成员函数的重载。
引用计数放在-1位置的好处是当字符串增长内容时,不用去调整引用计数存放的位置。

(2)cow在fork进程时的使用
在linux程序中,fork会产生一个与父进程彻底相同的子进程,子进程与父进程有着相同的代码段,数
据段,且堆栈也是指向父进程的物理空间的。这样在不考虑子进程中exec的状况,linux在fork时会使
用cow页实现,内核在fork时,并不复制整个进程地址空间,而是让父子进程共享一个拷贝。只有当需
要写入或者修改数据时才会使各个进程拥有本身的拷贝。也就是说,资源的复制操做会等到实际的个性
化需求到来时才会进行。函数

3.copy-on-write的一些缺陷或可能的陷阱
(1)线程安全有对性能的影响
cow在多线程环境中,须要处理竞态,而其中一引发关键在于:
a.原子操做应尽量细化,最好只涉及引用计数
b.应该先保证分配复制操做完整后,再高速引用计数
即便是原子操做也会存在一些对性能的影响问题,具体是不一样的体系结构在处理lock指令时,每每会锁
住比目标区域更大一些的地址空间,这样会形成一些其它数据的不可操做,影响性能。此外在多cpu状况
下还会出现一些cache-bounce的状况。具体见False sharing.
(2)可能的陷阱
主要体如今将string做为返回值处理时,注意后面的引用,特别是返回值声明为静态的状况。性能

附录:
主要是string的eager copy,cow,sso的三种实现。


优化

上图引用自 https://github.com/downloads/chenshuo/documents/CppPractice.pdfui

以上三种方式,eager copy的实现最简,其次sso,最麻烦的是cwo。不过也能够尝试将三种方式结合

起来设计string,根据字符串长度在处理时使用不一样的处理原则,调整时配置可以使性能达最优。

相关文章
相关标签/搜索