C ++ standart kütüphane iostreams'in yavaş performansından her bahsettiğimde, bir güvensizlik dalgası ile karşılaşıyorum. Yine de, iostream kütüphane kodunda (tam derleyici optimizasyonları) harcanan büyük miktarlarda zaman gösteren profiller sonuçlarına sahibim ve iostreams'ten OS'ye özgü I / O API'larına ve özel tampon yönetimine geçiş, bir büyüklük iyileştirme sırası veriyor.
C ++ standart kütüphanesinin yaptığı ekstra iş nedir, standart için gerekli midir ve pratikte faydalı mıdır? Yoksa bazı derleyiciler manuel arabellek yönetimi ile rekabet edebilecek iostream uygulamaları sunuyor mu?
Deneyler
Konuları harekete geçirmek için, iostreams iç ara belleğini kullanmak için birkaç kısa program yazdım:
- http://ideone.com/2PPYw içine ikili veri koymak
ostringstream
- bir
char[]
ara belleğe ikili veri koymak http://ideone.com/Ni5ct vector<char>
kullanarak http://ideone.com/Mj2Fi ikili veri koyarakback_inserter
- YENİ :
vector<char>
basit yineleyici http://ideone.com/9iitv - YENİ : İkili verileri doğrudan http://ideone.com/qc9QA içine koymak
stringbuf
- YENİ :
vector<char>
basit yineleyici artı sınırları kontrol http://ideone.com/YyrKy
ostringstream
Ve stringbuf
sürümlerinin çok daha yavaş olduğu için daha az yineleme kullandığını unutmayın .
İdeal olarak, + + ' ostringstream
dan yaklaşık 3 kat , ham bir tampondan yaklaşık 15 kat daha yavaştır . Gerçek uygulamamı özel arabelleğe geçirdiğimde, önceki ve sonraki profil oluşturma ile tutarlı hissediyor.std:copy
back_inserter
std::vector
memcpy
Bunların hepsi bellek içi arabelleklerdir, bu nedenle iostreams'in yavaşlığı yavaş disk G / Ç, çok fazla yıkama, stdio ile senkronizasyon veya insanların C ++ standart kütüphanesinin gözlemlenen yavaşlığını mazur göstermek için kullandıkları diğer şeylerden sorumlu tutulamaz. iostream.
Diğer sistemlerde karşılaştırmalar ve yaygın uygulamaların yaptığı şeyler (gcc's libc ++, Visual C ++, Intel C ++ gibi) ve genel giderlerin ne kadarının standart tarafından zorunlu kılındığına dair yorumları görmek güzel olurdu.
Bu test için gerekçe
Bazı insanlar iostream'lerin biçimlendirilmiş çıktı için daha yaygın olarak kullanıldığını doğru bir şekilde belirtti. Ancak, ikili dosya erişimi için C ++ standardı tarafından sağlanan tek modern API'dir. Ancak dahili arabellek üzerinde performans testleri yapmanın gerçek nedeni, tipik biçimlendirilmiş G / Ç için geçerlidir: iostreams disk denetleyicisini ham verilerle birlikte tutamazsa, biçimlendirmeden de sorumlu olduklarında nasıl devam edebilirler?
Karşılaştırma Zamanlaması
Bütün bunlar dış ( k
) döngüsünün tekrarı içindir.
İdeone'de (gcc-4.3.4, bilinmeyen işletim sistemi ve donanım):
ostringstream
: 53 milisaniyestringbuf
: 27 msvector<char>
veback_inserter
: 17,6 msvector<char>
sıradan yineleyici ile: 10.6 msvector<char>
yineleyici ve sınır kontrolü: 11.4 mschar[]
: 3,7 ms
Dizüstü bilgisayarımda (Visual C ++ 2010 x86,, cl /Ox /EHsc
Windows 7 Ultimate 64 bit, Intel Core i7, 8 GB RAM):
ostringstream
: 73.4 milisaniye, 71.6 msstringbuf
: 21,7 ms, 21,3 msvector<char>
veback_inserter
: 34,6 ms, 34,4 msvector<char>
sıradan yineleyici ile: 1.10 ms, 1.04 msvector<char>
yineleyici ve sınır kontrolü: 1,11 ms, 0,87 ms, 1,12 ms, 0,89 ms, 1,02 ms, 1,14 mschar[]
: 1,48 ms, 1,57 ms
Visual C ++ 2010 x86, Profil Kılavuzlu Optimizasyon cl /Ox /EHsc /GL /c
ile link /ltcg:pgi
, çalıştırın link /ltcg:pgo
, ölçün:
ostringstream
: 61,2 ms, 60,5 msvector<char>
sıradan yineleyici ile: 1.04 ms, 1.03 ms
Aynı dizüstü bilgisayar, aynı işletim sistemi, cygwin gcc 4.3.4 kullanarak g++ -O3
:
ostringstream
: 62,7 ms, 60,5 msstringbuf
: 44,4 ms, 44,5 msvector<char>
veback_inserter
: 13,5 ms, 13,6 msvector<char>
sıradan yineleyici ile: 4.1 ms, 3.9 msvector<char>
yineleyici ve sınır kontrolü: 4.0 ms, 4.0 mschar[]
: 3,57 ms, 3,75 ms
Aynı dizüstü bilgisayar, Visual C ++ 2008 SP1, cl /Ox /EHsc
:
ostringstream
: 88.7 ms, 87.6 msstringbuf
: 23,3 ms, 23,4 msvector<char>
veback_inserter
: 26,1 ms, 24,5 msvector<char>
sıradan yineleyici ile: 3.13 ms, 2.48 msvector<char>
yineleyici ve sınır kontrolü: 2,97 ms, 2,53 mschar[]
: 1,52 ms, 1,25 ms
Aynı dizüstü bilgisayar, Visual C ++ 2010 64 bit derleyici:
ostringstream
: 48,6 ms, 45,0 msstringbuf
: 16,2 ms, 16,0 msvector<char>
veback_inserter
: 26,3 ms, 26,5 msvector<char>
sıradan yineleyici ile: 0.87 ms, 0.89 msvector<char>
yineleyici ve sınır kontrolü: 0.99 ms, 0.99 mschar[]
: 1,25 ms, 1,24 ms
EDIT: Sonuçların ne kadar tutarlı olduğunu görmek için hepsini iki kez koştu. Oldukça tutarlı IMO.
NOT: Dizüstü bilgisayarımda, ideone'nin izin verdiğinden daha fazla CPU zamanı ayırabildiğim için, tüm yöntemler için yineleme sayısını 1000'e ayarladım. Bu araçlar olduğunu ostringstream
ve vector
yalnızca ilk geçişte gerçekleşir yeniden tahsis, nihai sonuçlar üzerinde çok az etkiye sahiptir.
DÜZENLEME: Hata, vector
sıradan yineleyici ile bir hata buldu , yineleyici gelişmiş değildi ve bu nedenle çok fazla önbellek isabet vardı. Nasıl vector<char>
daha iyi performans gösterdiğini merak ediyordum char[]
. Yine de çok fazla fark yaratmadı vector<char>
, hala char[]
VC ++ 2010'dan daha hızlı .
Sonuçlar
Çıktı akışlarının arabelleğe alınması, her veri eklendiğinde üç adım gerektirir:
- Gelen bloğun kullanılabilir arabellek alanına sığdığını kontrol edin.
- Gelen bloğu kopyalayın.
- Veri sonu işaretçisini güncelleyin.
Gönderdiğim son kod snippet'i, " vector<char>
basit yineleyici artı sınır kontrolü" sadece bunu yapmakla kalmaz, aynı zamanda ek alan ayırır ve gelen blok sığmadığında mevcut verileri taşır. Clifford'un işaret ettiği gibi, bir dosya G / Ç sınıfında arabelleğe almanın bunu yapmak zorunda kalmayacak, sadece mevcut arabelleği yıkayıp tekrar kullanacaktı. Bu nedenle, tamponlama çıktısının maliyetinde bir üst sınır olmalıdır. Ve tam olarak çalışan bir bellek içi tamponu yapmak için gereken şey budur.
Öyleyse stringbuf
ideone'de neden 2,5 kat daha yavaş ve test ettiğimde en az 10 kat daha yavaş? Bu basit mikro-ölçütte polimorfik olarak kullanılmıyor, bu yüzden açıklamıyor.
std::ostringstream
katlanarak tampon boyutunun yolu artıracak etkili yeterli değildir std::vector
I düşünmeye en (A) aptal ve (B) bir şey insanlar / O performansı hakkında düşünmek gerektiğini yapar. Her neyse, arabellek yeniden kullanılır, her seferinde yeniden tahsis edilmez. Ayrıca std::vector
dinamik olarak büyüyen bir tampon kullanıyor. Burada adil olmaya çalışıyorum.
ostringstream
ve olabildiğince hızlı performans istiyorsanız doğrudan gitmeyi düşünmelisiniz stringbuf
. ostream
Sınıflar aracılığıyla esnek tampon seçimi (dosya dize, vb) ile birlikte yerel farkında biçimlendirme işlevini kravat varsayalım rdbuf()
ve sanal fonksiyon arayüzünde. Herhangi bir biçimlendirme yapmıyorsanız, o zaman fazladan dolaylı aktarım seviyesi kesinlikle diğer yaklaşımlara kıyasla oransal olarak pahalı görünecektir.
ofstream
geçiş fprintf
yaparak düzen veya büyüklükteki hız artışlarını aldık . WinXPsp3 üzerinde MSVC 2008. iostreams sadece köpek yavaş.