Re: [問題] sizeof大小變動的class
這問題其實蠻有興趣的,早期私下有想過,我猜有其他較好的處理方式,
由於動作較低階,實作較複雜,過程大多只能以虛擬碼討論。
前半段為回應,後半段是小弟在較低階的想法,供討論、參考。
----------------------------------
回 應
※ 引述《oneill (安福虎之助)》之銘言:
: class data{
: public:
: int score;
: char *name;
: };
sizeof(class) 其實和 sizeof(struct) 一樣,只是裡面的 function 都沒算 size,
以此例而言, sizeof(data) = sizeof(int) + sizeof(char*) + padding
就像下面這段一樣
struct{
int score;
char * name;
};
: 然後用sizeof求大小
: sizeof(d1);
: sizeof(d2);
如 james~ 大所言,你要做的事情大概是要重寫一份
Constructor 及 operator = (assigned operator) ,
看任何一本 C++ 之書籍都會說到,
有用到 new / malloc 之類的東西就一定要寫兩部份
(1) Constructor, Destructor
(2) assigned operator (operator = )
另即使你要做 memcpy 時,真的做到的只有這部份
data a, b;
memcpy(&a, &b, sizeof(data);
a.score = b.score;
a.name = b.name;
只有複製 pointer ,如果你要做的是 n 筆資料,共享一份記憶體,這可以,
但相對的在釋放的時候只能放一次,不然釋放 n 次肯定出包。
這種沒辦法進一步連 name allocate 之 memory 深入複製
統稱 shallow copy,C/C++ 做的都只是 shallow copy。要達到你想要的只能這麼做
data& operator=(const data& d) const{
/* 先判斷 strlen(name) 夠不夠放 strlen(d.name), 做記憶體管理 */
strcpy(name, d.name);
score = d.score;
return *this;
}
這種連 heap 上之記憶體都深入複製的稱 deep copy,C\C++ 沒辦法做到這點,
所以只能自己動手刻。
當你自己實際動手刻完後會發現,你還是回歸到用固定大小,
或 C++ class string 好了,因在記憶體掌控效能方面,
基本上寫不怎贏 string 。
其他的可再找一下 deep copy / shallow copy 差異,
< C++ 不熟的話複習一下 operator overload >
------------------------------------
以下二個小技巧,是基於一信念 :
allocate / release 次數愈少愈好。
小 技 巧 1
我不確定實際上資料庫 txt field 通常是多長,
但可以確定的是,即使 txt field 不長,每個欄位給 char txt[256],
到了 10 萬筆資料 (10^5,這個數字在資料庫裡應還算是中小型資料庫) 時,
吃了約 25 MB, 這時候要塞到 stack 也很危險,就算真要固定欄位也該放在 heap 上。
依「課本」上 ( 其實我只是聽說的,不知道哪個課本) 的技巧,
(1) capacity > length 時, realloc, capacity*=2.
(2) capacity < 0.25*length 時, realloc, capacity/=2.
個人多加了一個技巧 : rate
因 capacity*=2 最後會愈來愈大,難保不會 allocate fail.
所以我是這麼做的
double multiple = 2.0;
double rate = 0.95;
size_t cap = 128 * multiple;
char *new_str;
do{
new_str = allocate(str, cap);
cap*=rate;
}while(rate > 1.0 && new_str==NULL)
if(new_str!=NULL) str = new_str;
甚至覺得 2.0 增長實在太快,所以策略換成
(1) capacity > length 時, realloc, capacity*=1.25.
(2) capacity < 0.64*length 時, realloc, capacity/=1.25.
< 0.64 = 1 / 1.25 / 1.25 >
但實際上有拆過 vector, string 都知道,在開 O2 後,他們根本都不這麼做,
不然就不會有 vector resize(0) 後,記憶體一直佔住的情況。
< 他們的策略是什麼我倒蠻好奇的,非到緊急關頭誓不放人 嗎? >
小 技 巧 2
雖說是「小技巧」,其實要完整實作很費時 ( 特別是以 C++ 完成及針對多行緒時 )
但我猜有被使用,甚至比我所提的、實作出來的更加成熟,
為方便討論,假設每個欄位長度限制是固定的 256 。
一開始 heap 上切一塊 pool 出來, pool = malloc(1U<<28) , 256MB,
一個 pool 有 256 MB, 所以它容許放 1M (1U<<20) 的資料量。
甚至這個 pool 用 linklist 較佳,因不夠的時候還可以再 allocate 另一塊,
而另一個 pool allocate 不出來的時候,再將 pool 改小一點 ,
(回到技巧一的動態縮減技巧)。
再來會有一個 bool used[1M] (可用 1bit 紀錄改善),紀錄該 field 是否有被使用過,
正在使用的話就放進去 used[i] 換成 true, 要刪掉時只要將 used[i] 改成 false,
used[i]==true 成立,代表 pool[256*i] ~ pool[256*(i+1)] 正被使用,
要取得也很簡單
char * get_str(size_t i) {
return pool + i*256;
/* return pool + (i<<8); */
}
但關鍵就變成了,怎麼知道那個 string 用的是 used[???]
這裡就可擅用 C++ class 特性了,原本的
class data{
int score ;
char * name;
};
就換成了
class data{
int score;
size_t used_idx;
};
如此只需在 constructor , destructor 時做 used 更新。
但問題又來了,如果將 txt field limit 拿掉的話呢?
這時需要其他的 array (linklist) 儲存資訊了,
如紀錄目前此 field 是從 pool[start : pos]、
pool 裡最大的連續空間是多少等等之類的,做起來反而更麻煩,
做不好的話讓 os 自己管理還比較好 。
-----------------------------------
以上一點意見,供參考。有誤請不吝更正。
--
「自從我學了 C# , 人都變聰明 , 考試都考一百分」
「自從我學了 VB , 皮膚都變好 , 人也變漂亮了 」
「自從我學了 Java , 明顯變壯 , 個子也變高了 」
「自從我學了 C++ , 內分泌失調 , 頭都禿了... 」
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 180.177.76.161
※ 編輯: EdisonX 來自: 180.177.76.161 (07/04 17:34)
推
07/04 20:25, , 1F
07/04 20:25, 1F
→
07/04 20:26, , 2F
07/04 20:26, 2F
→
07/04 20:30, , 3F
07/04 20:30, 3F
→
07/04 21:38, , 4F
07/04 21:38, 4F
→
07/04 21:39, , 5F
07/04 21:39, 5F
→
07/04 21:42, , 6F
07/04 21:42, 6F
→
07/04 21:43, , 7F
07/04 21:43, 7F
→
07/04 22:01, , 8F
07/04 22:01, 8F
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 2 之 2 篇):