2014-05-06 27 views
14

在C++ 11中引入了shrink_to_fit來補充某些STL容器(例如,std::vector,std::dequestd::string)。是shrink_to_fit將`std :: vector`減小到其大小的正確方法嗎?

Synopsizing,其主要功能是請求與之相關聯的容器減小其容量以適合其尺寸。然而,這個請求是不具有約束力的,並且容器實現可以自由地優化,否則將該向量的容量保留爲大於其大小。

此外,在以前的SO問題中,OP不鼓勵使用shrink_to_fit將其std::vector的容量減小到其大小。該理由不這樣做引述如下:

shrink_to_fit什麼也不做,或者給你緩存局部性的問題,它是O(n)與 執行(因爲你必須每個項目複製到其新的,小家)。 通常將內存留在內存中會更便宜。 @Massa

有人能這麼好心來解決以下問題:

  • 不要在報價參數持有?
  • 如果是,將STL容器容量縮小到其大小的正確方法是什麼(至少對於std::vector)。
  • 如果還有更好的方法來縮小容器,那麼shrink_to_fit究竟存在的原因是什麼?
+5

沒有一種便攜的方式可以100%可靠地獲得大小等於其容量的矢量。 'shrink_to_fit'禮貌地請求實施,以便儘可能地接近這個目標。一般來說,縮小容器沒有更好的辦法。 –

回答

13

引用中的參數是否成立?

措施,你會知道的。你是否被限制在內存中?你能在前面弄清楚正確的尺寸嗎?事實後,reserve會比收縮更高效。總的來說,我傾向於同意這樣一個前提,即大多數用途可能都適用於這種鬆懈。

如果是,將STL容器的容量縮小到其大小(至少對於std :: vector)的正確方法是什麼。

該評論不僅適用於shrink_to_fit,而且適用於任何其他縮小方式。考慮到你不能使用realloc,它涉及到獲取不同的內存塊並在那裏複製,無論你使用什麼機制進行縮小。

如果還有更好的方法來收縮容器,那麼shrink_to_fit是否存在的原因是什麼?

該請求不具有約束力,但替代方案沒有更好的保證。問題是是否縮小是有意義的,如果是這樣,則提供shring_to_fit操作是有意義的,該操作可以利用對象正在被移動到新位置的事實。即如果類型T有一個noexcept(true)移動構造函數,它將分配新內存並移動元素。

雖然您可以在外部實現相同的功能,但該接口簡化了操作。相當於shrink_to_fit在C++ 03將是:

std::vector<T>(current).swap(current); 

但是這種方法的問題是,當複製完成後到臨時不知道current將被更換,有沒有任何東西告訴圖書館它可以移動保存的對象。請注意,使用std::move(current)不會達到預期的效果,因爲它將移動到整個緩衝區,保持相同的capacity()

實現這個外部會有點更麻煩:

{ 
    std::vector<T> copy; 
    if (noexcept(T(std::move(declval<T>())))) { 
     copy.assign(std::make_move_iterator(current.begin()), 
        std::make_move_iterator(current.end())); 
    } else { 
     copy.assign(current.begin(), current.end()); 
    } 
    copy.swap(current); 
} 

假設我得到了,如果條件合適的......這可能不是你想要的,你想要這個動作進行一次寫的是什麼。

+1

*考慮到你不能重新分配,它涉及獲取不同的內存塊並在那裏複製,而不管你用什麼機制來縮小。*你能解釋一下嗎? realloc手冊頁提示realloc可以在原地工作(如果它將縮小內存區域,我會假設一個理智的實現)。這是由於C++語義的一些特點嗎? –

+1

@JonasWielicki:除了C++內存分配器不使用'realloc'。有一些工作是在boost內部的概念驗證中完成的,但分配器*不能使用'realloc'(或者說,他們可以使用'realloc',但只能實現'malloc'和'free') –

10
  • 參數是否會成立?

由於參數是原來我的,如果我保護他們不介意,一個接一個:

  1. 要麼shrink_to_fit什麼也不做(...)

    因爲它標準說(很多次,但在vector的情況下,它是23.3.7.3 ...),該該請求是非綁定的,允許實現緯度優化。這意味着實施可以將定義爲shrink_to_fit作爲無操作。

  2. (...)或它給你的緩存局部性問題

    在這種shrink_to_fit作爲無操作實現的情況下,你必須有能力size(),拷貝分配一個新的基礎容器(或者在最好的情況下,移動)構建所有新舊項目,破壞所有舊項目(在移動的情況下,這應該被優化,但是這可能涉及在舊容器上再次循環)然後破壞舊的容器本身。這樣做,在libstdc++-4.9,正如大衛·羅德里格斯已經描述的,通過

     _Tp(__make_move_if_noexcept_iterator(__c.begin()), 
          __make_move_if_noexcept_iterator(__c.end()), 
          __c.get_allocator()).swap(__c); 
    

    libc++-3.5,在__alloc_traits一個函數,它大致相同。

    哦,和實施絕對不能依靠realloc(即使它使用malloc::operator new其內存分配),因爲realloc,如果它不能就地收縮,要麼獨自離開內存(無操作)或者創建一個按位拷貝(並且錯過重新調整指針的機會,等適當的C++複製/移動構造函數會給出)。

    當然,可以編寫一個可收縮的內存分配器,並將其用於其向量的構造函數中。

    在向量大於緩存行的簡單情況下,所有這些移動都會對緩存施加壓力。

  3. ,它的O(n)的

    如果n = size(),我認爲這是上面建立的,起碼,你必須做一個n大小分配,n複製或移動結構,n破壞,並一個old_capacity大小的釋放。

  4. 通常它的便宜只是留在記憶鬆弛

    很明顯,除非你真的按下釋放內存(在這種情況下,它可能是明智的您的數據保存到磁盤並在稍後重新加載它需求...)

  • 如果是,什麼是萎縮STL容器的能力,它的大小適當的方式(至少對於的std ::向量)。

正確的方法仍然是shrink_to_fit ...你就必須要麼不依靠它還是非常清楚地知道你實現!

  • ,以及是否有更好的方式來收縮的容器,什麼是對shrink_to_fit後,所有存在的原因是什麼?

有沒有更好的辦法,但對於shrink_to_fit存在的原因是,AFAICT,有時你的程序可能會感到內存壓力,它的治療它的方法之一。不是一個很好的方式,但仍然。

HTH!

+1

我不記住:-)。我沒有提到你的名字是出於謹慎的原因。如果你想我可以編輯問題,並在報價單中提及你。 – 101010

+1

@ 40two,無論哪種方式都可以! – Massa

0
  • 如果是,什麼是(至少對於的std ::向量)萎縮STL容器的容量大小的正確方法。

在「交換特技」將修剪載體以所需的精確尺寸(從更有效的SQL):

vector<Person>(persons).swap(persons); 

特別有用的,當載體是空的,以釋放所有存儲器:

vector<Person>().swap(persons); 

由於保留了未使用空間的分配,向量不斷跳動我的單元測試程序的內存泄漏檢測代碼,並將其完美排序。

這是一種我真的不關心運行效率(大小或速度)的例子,但我關心的是確切的內存使用情況。

  • ,以及是否有更好的方式來收縮的容器,什麼是對shrink_to_fit後,所有存在的原因是什麼?

我真的不知道提供了一個功能,可以合法地做點絕對沒有什麼是什麼。 當我看到它被引入時,我歡呼,然後當我發現它不能被依賴時,絕望。

也許我們會在下一個版本中看到maybe_sort()。

+0

您也不能依賴交換機制。我不認爲(糾正我,如果我錯了)阻止新創建的矢量具有更大的容量,然後它的大小。但實際上,這兩種方法都有效。 – MikeMB

+0

不,我不認爲標準保證最初爲空的向量的容量爲0,儘管正如您在實踐中通常所說的那樣。然而,即使它在構建時不爲0,那麼很可能一個初始空向量與另一個最初爲空的向量具有相同的容量,因此爲了記憶比較目的,它仍然可以正常工作。 –

+0

我並不是主要談論空載體 - 任何導致動態內存分配的實現都是imho中斷的。我正在談論你作爲副本創建的臨時向量。由於底層堆分配函數的某些特殊性(例如,因爲它們只是以離散塊的形式分配內存塊),新創建的矢量可能具有比其大小更大的容量是非常有益的。 – MikeMB

相關問題