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