Std :: vector <int> 'in her değerini sıfırlamanın en hızlı yolu 0


Yanıtlar:


346
std::fill(v.begin(), v.end(), 0);

49
Montaj çıkışına bakıldığında, gcc aslında bu döngüyü sonuna kadar yaklaşana kadar bir seferde 16 bayta dökmek için mmx kayıtlarını kullanarak açar. Bunun oldukça hızlı olduğunu söyleyebilirim. Memset sürümü memset'e atlar, ki sanırım yaklaşık olarak hızlıdır. Ben senin yöntemini kullanırdım.
şeye kadir

Ancak, memset'e atlamak tek bir talimattır, bu yüzden kullanmak daha küçük bir ikili boyuta neden olacaktır.
Alexander Shishenko

2
OP'nin istediği tam olarak bu değil, ancak vektörünüzü aynı boyutta yeni bir tanesine yeniden atamak ( v = std::vector<int>(vec_size,0)) fillmakinemden biraz daha hızlı görünüyor
Yibo Yang

1
Bunu yapmanın en deyimsel yolu, kullanmaktan daha deyimsel assign.
alfC

1
yeni bir vektöre atamak yığın tahsisi yapar mı? ve sonra mevcut vektörün tahsisini at? Memset ve diğerlerinden daha yavaş olduğunu görebiliyordum
Conrad Jones

151

Her zaman olduğu gibi en hızlı sorulduğunda: Ölçün! Yukarıdaki Yöntemleri Kullanma (Clang kullanan bir Mac'te):

Method      |  executable size  |  Time Taken (in sec) |
            |  -O0    |  -O3    |  -O0      |  -O3     |  
------------|---------|---------|-----------|----------|
1. memset   | 17 kB   | 8.6 kB  | 0.125     | 0.124    |
2. fill     | 19 kB   | 8.6 kB  | 13.4      | 0.124    |
3. manual   | 19 kB   | 8.6 kB  | 14.5      | 0.124    |
4. assign   | 24 kB   | 9.0 kB  | 1.9       | 0.591    |

10000 ints vektöründe 100000 iterasyon kullanan.

Düzenleme: akla yatkın bu numaralar changeing çıkan kez değiştirirse sahip olabilir bazı güven (değil nihai montaj kod kontrolü olarak iyi gibi) yapay kriter tamamen uzak optimize edilmediğini. Elbette performansı gerçek koşullar altında bozmak en iyisidir. son Düzenle

referans için kullanılan kod:

#include <vector>

#define TEST_METHOD 1
const size_t TEST_ITERATIONS = 100000;
const size_t TEST_ARRAY_SIZE = 10000;

int main(int argc, char** argv) {

   std::vector<int> v(TEST_ARRAY_SIZE, 0);

   for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
   #if TEST_METHOD == 1 
      memset(&v[0], 0, v.size() * sizeof v[0]);
   #elif TEST_METHOD == 2
      std::fill(v.begin(), v.end(), 0);
   #elif TEST_METHOD == 3
      for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
         *it = 0;
      }
   #elif TEST_METHOD == 4
      v.assign(v.size(),0);
   #endif
   }

   return EXIT_SUCCESS;
}

Sonuç: kullanın std::fill(çünkü diğerleri en deyimsel demişlerdir)!


3
+1. Bu özel kriter kesin değildir, ancak nokta kesinlikle doğrudur, alternatiflerin gerçekte kullanılacakları için bir performans testi yazmalısınız. Performans farkı yoksa, o zaman en basit kaynak olanı kullanın.
Steve Jessop

3
“... kesin değil ...” IMO kendi başına bu sonuçsuzluğun kendisi için kıyaslama yapmak için zaten iyi bir nokta. Ve son
cümlenizi

4
UPDATE Testler için Nonius kullanma : clang3.6-libc ++ - c ++ 1y-O3 , gcc4.9-c ++ 1y-O3 ve gcc5-c ++ 1y-O3 - TL; DR : assignküçük kapasiteler hariç daha yavaştır üzerinde libc++. CODE coliru / paste
sehe

2
Ayrıca, vay, optimizasyon olmadan hızı önemsiyorsanız (bazı ekiplerin yaptığı 'hata ayıklama' modunda dağıtıyorsanız makul olabilir), fillkorkunç görünüyor. Öyle büyüklükte iki emir yavaş bu testte.
Kyle Strand

5
@KyleStrand: Doldurmanın korkunç olmadığı, bir şablon olduğu ve kodun çeviri biriminizde -O0 ile oluşturulduğu değil. Memset kullandığınızda, -O3 ile derlenen libc kodunu kullanırsınız (kodunuzu -O0 ile derleseniz bile). Hata ayıklamadaki hızı önemsiyorsanız ve şablonlar kullanıyorsanız, -O3
Tic

25

assignÜye işlevi nasıl olur ?

some_vector.assign(some_vector.size(), 0);

2
OP mevcut değerleri sıfırlamak istedi, ancak değerleri yeniden boyutlandırmak ve sıfırlamak istediğinizde cevabınız daha iyi . Teşekkürler!

15

Sadece tamsayıların bir vektörüyse, ilk önce şunu denerdim:

memset(&my_vector[0], 0, my_vector.size() * sizeof my_vector[0]);

Çok C ++ değil, bu yüzden eminim birisi bunu yapmak için uygun bir yol sağlayacaktır. :)


3
Standart (2003 TC1) bir std :: vektörün bellekte bitişik olduğunu garanti ettiğinden, bu iyi olmalıdır. C ++ kitaplığınız 2003 TC1 ile uyumlu değilse, bunu kullanmayın.
Mario

2
@Mario: Tabii ki bu doğru ve iyi bilindiği varsayılmadıkça bunu yayınlamazdım. :) Ama teşekkürler.
gevşeyin

1
Montajı kontrol ettim. ::std::fillKod-bloaty tarafında biraz hepsi satır içi olduğundan olsa yöntem, oldukça hızlı darned gelen bir özellik genişler. Yine de kullanardım çünkü okumak çok daha güzel.
şeye kadir

4
Vektörün boş olup olmadığını kontrol etmeyi ve bu durumda hiçbir şey yapmamalısınız. Boş vektör için & buf [0] hesaplanması STL kodunda iddialar oluşturabilir.
Sergey

4

Deneyin

std::fill

ve ayrıca

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);

yeniden boyutlandırma çok güzel
Nick

4

Aynı soru vardı ama oldukça kısa vector<bool>(afaik standart dahili bir boolean öğeleri sürekli dizi daha farklı uygulamayı sağlar). Bu yüzden Fabio Fracassi tarafından hafifçe değiştirilmiş testleri tekrarladım. Sonuçlar aşağıdaki gibidir (kez, saniye cinsinden):

            -O0       -O3
         --------  --------
memset     0.666     1.045
fill      19.357     1.066
iterator  67.368     1.043
assign    17.975     0.530
for i     22.610     1.004

Görünüşe göre bu boyutlar vector<bool>::assign()için daha hızlı. Testler için kullanılan kod:

#include <vector>
#include <cstring>
#include <cstdlib>

#define TEST_METHOD 5
const size_t TEST_ITERATIONS = 34359738;
const size_t TEST_ARRAY_SIZE = 200;

using namespace std;

int main(int argc, char** argv) {

    std::vector<int> v(TEST_ARRAY_SIZE, 0);

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
#if TEST_METHOD == 1
        memset(&v[0], false, v.size() * sizeof v[0]);
#elif TEST_METHOD == 2
        std::fill(v.begin(), v.end(), false);
   #elif TEST_METHOD == 3
        for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
            *it = 0;
        }
   #elif TEST_METHOD == 4
      v.assign(v.size(),false);
   #elif TEST_METHOD == 5
      for (size_t i = 0; i < TEST_ARRAY_SIZE; i++) {
          v[i] = false;
      }
#endif
    }

    return EXIT_SUCCESS;
}

Ubuntu 17.10'da GCC 7.2.0 derleyicisini kullandım. Derleme için komut satırı:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.