C++ 是很恐怖的,為了速度,它可以違背自己的語意。但看熟了,也就習慣了。
C++ 是很恐怖的,為了速度,它可以違背自己的語意。但看熟了,也就習慣了。
這叫作 copy elision (或 return value optimization)。你明明實作了物件的 copy constructor,也在函式內建立自動物件然後傳回,但 copy elision 允許 C++ compiler 編出不呼叫 copy constructor 的程式。
這叫作 copy elision (或 return value optimization)。你明明實作了物件的 copy constructor,也在函式內建立自動物件然後傳回,但 copy elision 允許 C++ compiler 編出不呼叫 copy constructor 的程式。
如果使用 std::shared_ptr,就可以解開 std::unique_ptr 的限制。當然,更多的功能就會帶來更多的問題。
如果使用 std::shared_ptr,就可以解開 std::unique_ptr 的限制。當然,更多的功能就會帶來更多的問題。
代價是在傳遞 unique_ptr 的時候,ownership 會一起被傳走。
代價是在傳遞 unique_ptr 的時候,ownership 會一起被傳走。
因為不能複製,所以若不是空指標,就是唯一的 owner。單純而有效。
因為不能複製,所以若不是空指標,就是唯一的 owner。單純而有效。
另外,在 C++11 以後,STL 開始支援 move semantics,允許在 container 之間移動資源,可以在必要的時候減少複製昂貴 (消耗資源) 的物件。但 swap() 還是很有用的寫法。也許一開始看會覺得奇怪,但熟悉了 C++ 物件生命周期以後,可以用它寫出清楚易懂的程式。
另外,在 C++11 以後,STL 開始支援 move semantics,允許在 container 之間移動資源,可以在必要的時候減少複製昂貴 (消耗資源) 的物件。但 swap() 還是很有用的寫法。也許一開始看會覺得奇怪,但熟悉了 C++ 物件生命周期以後,可以用它寫出清楚易懂的程式。
至於為什麼會多配置那麼多記憶體,讀一下 std::vector 的實作就會知道。不難讀,而且每個編譯器都會附原始碼。
至於為什麼會多配置那麼多記憶體,讀一下 std::vector 的實作就會知道。不難讀,而且每個編譯器都會附原始碼。
STL container 的記憶體是由 allocator 控制的,我們可以寫個簡單的 STL allocator 來作一下實驗,紀錄 STL container 配置以及釋放的記憶體。Allocator 內部可以用 new/delete,但也可以不用。
STL container 的記憶體是由 allocator 控制的,我們可以寫個簡單的 STL allocator 來作一下實驗,紀錄 STL container 配置以及釋放的記憶體。Allocator 內部可以用 new/delete,但也可以不用。

在呼叫 malloc 的場合,外部無從得知記憶體是從堆積 (heap) 或 mmap 配置而來,因此一定需要再作一次 memset(0),才能確定。
在配置大塊全零值記憶體的時候,calloc 會比 malloc + memset 更快。
愈了解電腦系統的運作方式,程式可以寫得愈好。

在呼叫 malloc 的場合,外部無從得知記憶體是從堆積 (heap) 或 mmap 配置而來,因此一定需要再作一次 memset(0),才能確定。
在配置大塊全零值記憶體的時候,calloc 會比 malloc + memset 更快。
愈了解電腦系統的運作方式,程式可以寫得愈好。
malloc/free 就能處理大部分的記憶體管理工作,但其它 API 也有用處,例如 aligned_alloc 能用來配置 SIMD 所需要的對齊 (aligned) 記憶體區塊。
malloc/free 就能處理大部分的記憶體管理工作,但其它 API 也有用處,例如 aligned_alloc 能用來配置 SIMD 所需要的對齊 (aligned) 記憶體區塊。
原因顯而易見。delete 會呼叫解構子,再釋放記憶體。但 placement new 建構的物件,記憶體不是自己配置的,所以不能自己釋放。
原因顯而易見。delete 會呼叫解構子,再釋放記憶體。但 placement new 建構的物件,記憶體不是自己配置的,所以不能自己釋放。
在跨模組的場合,會使用 placement new建構物件。因為所建構的物件位於其它模組所管理的記憶體之上,所以不能在建構之前自行配置記憶體。像 boost.python 和 pybind11 這種動態語言的包裝工具,就需要這種用法。
在跨模組的場合,會使用 placement new建構物件。因為所建構的物件位於其它模組所管理的記憶體之上,所以不能在建構之前自行配置記憶體。像 boost.python 和 pybind11 這種動態語言的包裝工具,就需要這種用法。
如果直接在 stack 上建構物件而沒有使用動態記憶體配置,就不需要 new/delete。automatic storage duration 會確保脫離 scope 的時候呼叫物件的解構子。
如果直接在 stack 上建構物件而沒有使用動態記憶體配置,就不需要 new/delete。automatic storage duration 會確保脫離 scope 的時候呼叫物件的解構子。
為了在 dynamic storage duration 的場合處理物件的建構子 (constructor) 與解構子 (destructor),C++ 提供一對 new/delete expression。new (expression) 負責呼叫 ::operator::new 配置記憶體,然後再呼叫物件建構子。delete (expression) 則先呼叫物件解構子,再呼叫 ::operator::delete 釋放記憶體。
為了在 dynamic storage duration 的場合處理物件的建構子 (constructor) 與解構子 (destructor),C++ 提供一對 new/delete expression。new (expression) 負責呼叫 ::operator::new 配置記憶體,然後再呼叫物件建構子。delete (expression) 則先呼叫物件解構子,再呼叫 ::operator::delete 釋放記憶體。
相似的型別會需要相似的單元測試程式。寫起來重複乏味,但可以忍受。真正麻煩的是這些測試程式長得太像,容易多改、漏改,或是改錯。
這可以解。設計一組基底類別,為相似的型別提供別名即可。
測試用的 Python 程式碼寫在衍生類別裡面,就會回復到只有一套。也就是說,由基底類別負責管理型別與精度的差異。
相似的型別會需要相似的單元測試程式。寫起來重複乏味,但可以忍受。真正麻煩的是這些測試程式長得太像,容易多改、漏改,或是改錯。
這可以解。設計一組基底類別,為相似的型別提供別名即可。
測試用的 Python 程式碼寫在衍生類別裡面,就會回復到只有一套。也就是說,由基底類別負責管理型別與精度的差異。
為了掌握複雜的觀念和邏輯,人腦需要抓住一些記憶點。看到熟習的程式的時候,會回想那些重點。這些重點的內容和編排方式就決定了程式的結構。
std::vector 是個蠻好的例子。一旦知道要用三個指標實作 std::vector,就會把它的行為特性印到腦袋裡面。這三個指標是很好的實作,很難不用,也很容易用到其它地方。這種就是好程式。
為了掌握複雜的觀念和邏輯,人腦需要抓住一些記憶點。看到熟習的程式的時候,會回想那些重點。這些重點的內容和編排方式就決定了程式的結構。
std::vector 是個蠻好的例子。一旦知道要用三個指標實作 std::vector,就會把它的行為特性印到腦袋裡面。這三個指標是很好的實作,很難不用,也很容易用到其它地方。這種就是好程式。
也就是說平均每一兩天往 GitHub 送 (public) commit 的使用者,在台灣不到兩百個。這樣太少。
起碼來說,先不要談品質,資訊系學生送 patch 的密度不能更低,不然程式編寫的練習量是不夠的。
也就是說平均每一兩天往 GitHub 送 (public) commit 的使用者,在台灣不到兩百個。這樣太少。
起碼來說,先不要談品質,資訊系學生送 patch 的密度不能更低,不然程式編寫的練習量是不夠的。
不管要作什麼事情,寫程式也一樣,記著一定要盯著看。沒有人盯著看,事情一定作不好。就算是簡單的動作,不專心也容易出錯。複雜的工程業務更是如此。需要有人驅動 (drive) 大家作事,效率才會高。
有在健身的人一定會同意,有教練在盯和自己練習,效率天差地遠,所以教練課雖然貴,大家還是願意付錢,它有這個價值。
如果不強制驅動,工作不會有效率。人性趨向省錢省事,能坐就不想站,能躺就不想坐。如果沒有人管,大家一定會便宜行事。就好像不抓違規停車的地方,就算停車場就空在旁邊,大家仍然會停在紅線上。
不管要作什麼事情,寫程式也一樣,記著一定要盯著看。沒有人盯著看,事情一定作不好。就算是簡單的動作,不專心也容易出錯。複雜的工程業務更是如此。需要有人驅動 (drive) 大家作事,效率才會高。
有在健身的人一定會同意,有教練在盯和自己練習,效率天差地遠,所以教練課雖然貴,大家還是願意付錢,它有這個價值。
如果不強制驅動,工作不會有效率。人性趨向省錢省事,能坐就不想站,能躺就不想坐。如果沒有人管,大家一定會便宜行事。就好像不抓違規停車的地方,就算停車場就空在旁邊,大家仍然會停在紅線上。
強者我朋友最近送了一個 patch,幫一段程式加速十八倍。
改動的程式其實很少,重點是拿掉 accessor,直接操作指標,這就能消除很多 overhead。然後把迴圈拆成兩個,也有點幫助。
這種 tight loop 寫得多了以後,其實蠻容易直接寫成快速的版本。不過偶爾還是會忘記,所以也不必逼自己人腦最佳化。先寫個版本,跑一下 profile,很快就能找到 performance hotspot,然後對症下藥,不管對程式熟不熟,都一樣有效。
強者我朋友最近送了一個 patch,幫一段程式加速十八倍。
改動的程式其實很少,重點是拿掉 accessor,直接操作指標,這就能消除很多 overhead。然後把迴圈拆成兩個,也有點幫助。
這種 tight loop 寫得多了以後,其實蠻容易直接寫成快速的版本。不過偶爾還是會忘記,所以也不必逼自己人腦最佳化。先寫個版本,跑一下 profile,很快就能找到 performance hotspot,然後對症下藥,不管對程式熟不熟,都一樣有效。