簡體   English   中英

shrink_to_fit是減少`std :: vector`到其大小的容量的正確方法嗎?

[英]Is shrink_to_fit the proper way of reducing the capacity a `std::vector` to its size?

在C ++ 11中,引入了shrink_to_fit來補充某些STL容器(例如, std::vectorstd::dequestd::string )。

Synopsizing,其主要功能是請求與之關聯的容器, 以減少其容量以適應其大小 但是,此請求是非綁定的 ,並且容器實現可以自由地進行優化,並使向量的容量大於其大小。

此外,在之前的SO問題中,不鼓勵OP使用shrink_to_fit來降低他的std::vector的容量。 不這樣做的理由如下:

shrink_to_fit什么都不做,或者它給你緩存局部性問題,並且執行O(n)(因為你必須將每個項目復制到他們新的較小的家庭)。 通常,將松弛留在內存中會更便宜。 @Massa

有人可以如此善意地解決以下問題:

  • 報價中的參數是否成立?
  • 如果是 ,那么將STL容器的容量縮小到其大小的正確方法什么(至少對於std::vector )。
  • 如果有一個更好的方法來縮小容器,那么后來存在shrink_to_fit的原因是什么?

報價中的參數是否成立?

措施,你會知道。 你是否受限於記憶? 你能預先確定正確的尺寸嗎? reserve比在事后收縮更有效。 總的來說,我傾向於同意這樣的前提,即大多數用途可能都很好。

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

評論不僅適用於shrink_to_fit ,還適用於任何其他縮小方式。 鑒於您無法重新realloc ,它涉及獲取不同的內存塊並在那里進行復制,無論您使用什么機制進行收縮。

如果有一個更好的方法來縮小容器,那么后來存在shrink_to_fit的原因是什么?

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

雖然您可以在外部實現相同的功能,但此界面可簡化操作。 相當於C ++ 03中的shrink_to_fit本來就是:

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);
}

假設我得到if條件正確...這可能不是你想要在每次想要這個操作時寫的東西。

  • 論證會持有嗎?

由於爭論最初是我的,所以不要介意我一個接一個地為他們辯護:

  1. shrink_to_fit什么都不做(......)

    正如所提到的那樣,標准說(很多次,但在vector的情況下,它是第23.3.7.3節......) 請求是非綁定的,以允許實現緯度進行優化 這意味着實現可以shrink_to_fit定義為no-op。

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

    如果shrink_to_fit 沒有實現為無操作,則必須分配一個容量size()的新基礎容器,復制(或者,在最好的情況下,移動)構造所有N = size()新項目舊的,破壞所有舊的(在移動的情況下,這應該被優化,但這可能涉及在舊容器上再次循環)然后破壞舊容器本身。 這是在libstdc++-4.9 ,正如David Rodriguez所描述的那樣

      _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 inside ::operator new )因為realloc ,如果它不能就地收縮,將會留下內存(無操作情況)或做一個按位復制(並錯過重新調整指針的機會等,正確的C ++復制/移動構造函數會給出)。

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

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

  3. 它是O(n)

    如果n = size() ,我認為它已經建立在上面,至少,你必須做一個n大小的分配, n復制或移動結構, n析構和一個old_capacity大小的釋放。

  4. 通常只是將松弛留在記憶中更便宜

    顯然,除非你真的要求免費內存(在這種情況下,將數據保存到磁盤並在以后按需重新加載它可能更明智......)

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

正確的方法仍然是shrink_to_fit ......你只需要不依賴它或者非常了解你的實現!

  • 如果有一個更好的方法來縮小容器,那么后來存在shrink_to_fit的原因是什么?

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

HTH!

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

'swap trick'會將向量修剪為所需的確切大小(來自更有效的SQL):

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

當向量為空時特別有用,釋放所有內存:

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

由於保留了未使用空間的分配,矢量不斷地絆倒我的單元測試器的內存泄漏檢測代碼,並且這完全排除了它們。

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

  • 如果有一個更好的方法來縮小容器,那么后來存在shrink_to_fit的原因是什么?

我真的不知道提供一個可以合法地完成任何功能的功能的重點是什么。 當我看到它被引入時,我歡呼,然后當我發現它無法依賴時,我感到絕望。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM