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,根據字符串長度在處理時使用不一樣的處理原則,調整時配置可以使性能達最優。

相關文章
相關標籤/搜索