Adakah yang bisa mengalahkan kinerja integer saya ke kode std :: string, yang ditautkan di bawah?
Sudah ada beberapa pertanyaan yang menjelaskan cara mengubah integer menjadi a std::string
di C ++, seperti ini , tetapi tidak ada solusi yang diberikan yang efisien.
Berikut adalah kode siap-kompilasi untuk beberapa metode umum untuk bersaing:
- The "C ++ way", menggunakan stringstream: http://ideone.com/jh3Sa
- sprintf, yang biasanya direkomendasikan oleh para SO-ers untuk yang sadar kinerja: http://ideone.com/82kwR
Berlawanan dengan kepercayaan populer , boost::lexical_cast
memiliki penerapannya sendiri ( kertas putih ) dan tidak menggunakan stringstream
dan operator penyisipan numerik. Saya sangat ingin melihat kinerjanya dibandingkan, karena pertanyaan lain ini menunjukkan bahwa ini menyedihkan .
Dan kontribusi saya sendiri, yang kompetitif di komputer desktop, dan mendemonstrasikan pendekatan yang berjalan dengan kecepatan penuh pada sistem tertanam juga, tidak seperti algoritma yang bergantung pada modulo integer:
- Algoritma Ben: http://ideone.com/SsEUW
Jika Anda ingin menggunakan kode itu, saya akan membuatnya tersedia dengan lisensi BSD yang disederhanakan (penggunaan komersial diperbolehkan, atribusi diperlukan). Tanya saja.
Terakhir, fungsinya ltoa
tidak standar tetapi tersedia secara luas.
- versi ltoa, untuk siapa saja yang memiliki kompiler yang menyediakannya (ideone tidak): http://ideone.com/T5Wim
Saya akan memposting pengukuran kinerja saya sebagai jawaban segera.
Aturan untuk algoritme
- Berikan kode untuk konversi setidaknya bilangan bulat 32-bit dan unsigned menjadi desimal.
- Menghasilkan keluaran sebagai a
std::string
. - Tidak ada trik yang tidak kompatibel dengan threading dan sinyal (misalnya, buffer statis).
- Anda dapat mengasumsikan kumpulan karakter ASCII.
- Pastikan untuk menguji kode Anda pada
INT_MIN
mesin pelengkap dua di mana nilai absolut tidak dapat direpresentasikan. - Idealnya, keluarannya harus karakter-untuk-karakter yang identik dengan versi C ++ kanonik menggunakan
stringstream
, http://ideone.com/jh3Sa , tetapi apa pun yang jelas dapat dimengerti sebagai nomor yang benar juga oke. - BARU : Meskipun Anda dapat menggunakan opsi kompilator dan pengoptimal apa pun (kecuali sepenuhnya dinonaktifkan) yang Anda inginkan untuk perbandingan, kode juga perlu mengkompilasi dan memberikan hasil yang benar di bawah setidaknya VC ++ 2010 dan g ++.
Diharapkan untuk Diskusi
Selain algoritme yang lebih baik, saya juga ingin mendapatkan beberapa tolok ukur pada beberapa platform dan kompiler yang berbeda (mari gunakan throughput MB / s sebagai unit ukuran standar kami). Saya percaya bahwa kode untuk algoritme saya (saya tahu sprintf
tolok ukur mengambil beberapa pintasan - sekarang sudah diperbaiki) adalah perilaku yang ditentukan dengan baik oleh standar, setidaknya di bawah asumsi ASCII, tetapi jika Anda melihat perilaku atau input yang tidak ditentukan yang outputnya tidak valid, harap tunjukkan itu.
Kesimpulan:
Algoritme yang berbeda bekerja untuk g ++ dan VC2010, kemungkinan karena penerapan yang berbeda std::string
pada masing-masing. VC2010 jelas melakukan pekerjaan yang lebih baik dengan NRVO, menyingkirkan nilai balik yang hanya dibantu di gcc.
Kode ditemukan yang mengungguli sprintf
dengan urutan besarnya. ostringstream
tertinggal dengan faktor 50 dan lebih.
Pemenang tantangan ini adalah pengguna434507 yang menghasilkan kode yang menjalankan 350% kecepatan saya sendiri di gcc. Entri lebih lanjut ditutup karena keinginan komunitas SO.
Juara kecepatan (final?) Saat ini adalah:
- Untuk gcc: user434507, 8 kali lebih cepat dari
sprintf
: http://ideone.com/0uhhX - Untuk Visual C ++: Timo, 15 kali lebih cepat dari
sprintf
: http://ideone.com/VpKO3
sumber
Jawaban:
Ini akan meledak pada sistem yang melarang akses memori yang tidak selaras (dalam hal ini, tugas pertama yang tidak selaras melalui
*(short*)
akan menyebabkan segfault), tetapi seharusnya bekerja dengan sangat baik jika tidak.Salah satu hal penting yang harus dilakukan adalah meminimalkan penggunaan
std::string
. (Ironis, saya tahu.) Dalam Visual Studio, misalnya, kebanyakan panggilan ke metode std :: string tidak sebaris, bahkan jika Anda menetapkan / Ob2 dalam opsi kompilator. Jadi, bahkan sesuatu yang sepele seperti panggilan kestd::string::clear()
, yang mungkin Anda perkirakan akan sangat cepat, dapat menggunakan 100 clockticks saat menghubungkan CRT sebagai library statis, dan sebanyak 300 clockticks saat menghubungkan sebagai DLL.Untuk alasan yang sama, mengembalikan melalui referensi lebih baik karena menghindari penugasan, konstruktor, dan destruktor.
sumber
sprintf
. Dan dengan VC ++ 2010, ia mendapat sekitar 50 MB / s, sekitar dua kali kecepatan sprintf.sprintf
implementasi, saya sudah menyebutkannya dalam pertanyaan saya, tetapi saya yakin kode-ke-ketukan memberikan hasil yang persis sama dengan stringstream.sprintf
pembungkusnya juga, untuk menghindari kebingungan.Ah, tantangan luar biasa ngomong-ngomong ... Aku bersenang-senang dengan ini.
Saya memiliki dua algoritme untuk dikirimkan (kode ada di bagian bawah jika Anda ingin melewatkannya). Dalam perbandingan saya, saya mengharuskan fungsi mengembalikan string dan dapat menangani int dan unsigned int. Membandingkan hal-hal yang tidak membentuk string dengan yang tidak benar-benar masuk akal.
Yang pertama adalah implementasi menyenangkan yang tidak menggunakan tabel pencarian yang dihitung sebelumnya atau pembagian / modulo eksplisit. Yang ini kompetitif dengan yang lain dengan gcc dan dengan semua kecuali Timo di pnidui (untuk alasan bagus yang saya jelaskan di bawah). Algoritme kedua adalah pengajuan saya yang sebenarnya untuk kinerja tertinggi. Dalam pengujian saya, ini mengalahkan semua yang lain di gcc dan pnidui.
Saya rasa saya tahu mengapa beberapa hasil di MSVC sangat bagus. std :: string memiliki dua konstruktor yang relevan
std::string(char* str, size_t n)
dan
std::string(ForwardIterator b, ForwardIterator e)
gcc melakukan hal yang sama untuk keduanya ... yaitu menggunakan yang kedua untuk mengimplementasikan yang pertama. Konstruktor pertama dapat diimplementasikan secara signifikan lebih efisien dari itu dan MSVC melakukannya. Manfaat sampingan dari ini adalah bahwa dalam beberapa kasus (seperti kode cepat saya dan kode Timo) konstruktor string dapat disisipkan. Faktanya, hanya beralih antara konstruktor ini di MSVC hampir perbedaan 2x lipat untuk kode saya.
Hasil pengujian kinerja saya:
Sumber Kode:
- Voigt
- Timo
- ergosys
- user434507
- user-voigt-timo
- hopman-fun
- hopman-fast
gcc 4.4.5 -O2 di Ubuntu 10.10 64-bit, Core i5
MSVC 2010 64-bit / Ox pada Windows 7 64-bit, Core i5
Berikut adalah beberapa hasil dan kerangka pengujian / waktu pada ideone
http://ideone.com/XZRqp
Perhatikan bahwa ideone adalah lingkungan 32-bit. Kedua algoritme saya menderita karenanya, tetapi hopman_fast setidaknya masih kompetitif.
Perhatikan bahwa untuk dua atau lebih yang tidak membuat string saya menambahkan template fungsi berikut:
Sekarang untuk kode saya ... pertama yang menyenangkan:
Lalu yang cepat:
sumber
Data tolok ukur untuk kode yang diberikan dalam pertanyaan:
Di ideone (gcc 4.3.4):
Core i7, Windows 7 64-bit, RAM 8 GB, Visual C ++ 2010 32-bit:
cl /Ox /EHsc
Core i7, Windows 7 64-bit, RAM 8 GB, Visual C ++ 2010 64-bit:
cl /Ox /EHsc
Core i7, Windows 7 64-bit, RAM 8 GB, cygwin gcc 4.3.4:
g++ -O3
edit : Saya akan menambahkan jawaban saya sendiri, tetapi pertanyaannya telah ditutup jadi saya menambahkannya di sini. :) Saya menulis algoritme saya sendiri dan berhasil mendapatkan peningkatan yang layak atas kode Ben, meskipun saya hanya mengujinya di MSVC 2010. Saya juga membuat tolok ukur dari semua implementasi yang disajikan sejauh ini, menggunakan pengaturan pengujian yang sama dengan yang ada di aslinya Ben kode. - Timo
Intel Q9450, Win XP 32bit, MSVC 2010
cl /O2 /EHsc
-
sumber
std::string
atau pengoptimalan kode aritmatika yang buruk. Saya akan membuat versi lain yangstd::string
pada akhirnya tidak mengonversi menjadi dan melihat apakah tarif gcc lebih baik.Meskipun info yang kami dapatkan di sini untuk algoritme cukup bagus, menurut saya pertanyaannya adalah "rusak", dan saya akan menjelaskan mengapa saya berpikir demikian:
Pertanyaan tersebut meminta untuk mengambil kinerja
int
->std::string
konversi, dan ini mungkin menarik saat membandingkan metode yang umum tersedia, seperti implementasi stringstream yang berbeda atau boost :: lexical_cast. Namun, tidak masuk akal ketika meminta kode baru , algoritme khusus, untuk melakukan ini. Alasannya adalah bahwa int2string akan selalu melibatkan alokasi heap dari std :: string dan jika kita mencoba memeras yang terakhir dari algoritma konversi kita, menurut saya tidak masuk akal untuk mencampur pengukuran ini dengan alokasi heap yang dilakukan oleh std: :tali. Jika saya ingin konversi performant, saya akan selalu menggunakan buffer ukuran tetap dan tentu saja tidak pernah mengalokasikan apa pun di heap!Singkatnya, menurut saya pengaturan waktunya harus dibagi:
Aspek-aspek tersebut jangan sampai tercampur dalam satu waktu, IMHO.
sumber
std::string
" ditempatkan di sana hanya untuk membuat semuanya adil dan konsisten untuk semua kiriman. Algoritme yang lebih cepat membuatstd::string
hasil juga akan lebih cepat mengisi buffer yang sudah dialokasikan sebelumnya.Saya tidak dapat menguji di bawah VS, tetapi ini tampaknya lebih cepat daripada kode Anda untuk g ++, sekitar 10%. Mungkin bisa disetel, nilai keputusan yang dipilih adalah tebakan. int saja, maaf.
sumber
char
keunsigned
menghasilkan peningkatan kecepatan yang sama dalam kode saya, setidaknya pada gcc / ideone ideone.com/uthKK . Saya akan menguji VS besok.Memperbarui jawaban pengguna2985907 ... modp_ufast ...
sumber
Mismatch found: Generated: -99 Reference: -9099999 Mismatch found: Generated: -99 Reference: -9099998 Mismatch found: Generated: -99 Reference: -9099997
Inilah percobaan kecil saya dari teka-teki yang menyenangkan ini.
Alih-alih menggunakan tabel pencarian, saya ingin kompiler untuk mencari tahu semuanya. Dalam kasus ini khususnya - jika Anda membaca Hackers 'Delight, Anda akan melihat cara kerja divide dan modulo - yang membuatnya sangat mungkin untuk dioptimalkan menggunakan instruksi SSE / AVX.
Tolok ukur kinerja
Untuk kecepatan, tolok ukur saya di sini memberi tahu saya bahwa ini 1,5 kali lebih cepat daripada kerja Timo (pada Intel Haswell saya berjalan pada sekitar 1 GB / s).
Hal-hal yang bisa Anda anggap curang
Adapun cheat not-making-a-std-string yang saya gunakan - tentu saja saya mempertimbangkannya untuk benchmark saya terhadap metode Timo juga.
Saya menggunakan intrinsik: BSR. Jika Anda suka, Anda juga dapat menggunakan tabel DeBruijn - yang merupakan salah satu hal yang saya tulis sedikit tentang posting '2log tercepat' saya. Tentu saja, ini memang memiliki penalti kinerja (* yah ... jika Anda melakukan banyak operasi itoa, Anda sebenarnya dapat membuat BSR lebih cepat tetapi saya rasa itu tidak adil ...).
Cara kerjanya
Hal pertama yang harus dilakukan adalah mencari tahu berapa banyak memori yang kita butuhkan. Ini pada dasarnya adalah 10log, yang dapat diterapkan dengan berbagai cara cerdas. Lihat " Bit Twiddling Hacks " yang sering dikutip untuk detailnya.
Hal berikutnya yang harus dilakukan adalah menjalankan keluaran numerik. Saya menggunakan rekursi template untuk ini, jadi kompilator akan mengetahuinya.
Saya menggunakan 'modulo' dan 'div' tepat di samping satu sama lain. Jika Anda membaca Hacker's Delight, Anda akan melihat bahwa keduanya terkait erat, jadi jika Anda memiliki satu jawaban, Anda mungkin juga memiliki jawaban yang lain. Saya pikir kompiler dapat mengetahui detailnya ... :-)
Kode
Mendapatkan jumlah digit menggunakan log (dimodifikasi )10:
Dapatkan sendiri stringnya:
sumber
Saya sudah duduk-duduk sebentar dan akhirnya sempat mempostingnya.
Beberapa metode lebih banyak dibandingkan dengan kata ganda sekaligus hopman_fast . Hasilnya adalah untuk std :: string yang dioptimalkan untuk string pendek GCC karena jika tidak, perbedaan kinerja dikaburkan oleh overhead kode manajemen string salin-saat-tulis. Throughput diukur dengan cara yang sama seperti di tempat lain dalam topik ini, jumlah siklus untuk bagian serialisasi mentah dari kode sebelum menyalin buffer keluaran ke dalam string.
Sakelar waktu kompilasi:
-DVSTRING - mengaktifkan string SSO untuk penyiapan GCC yang lebih lama
-DBSR1 - mengaktifkan log
cepat10 -DRDTSC - mengaktifkan penghitung siklus
sumber
Saya yakin saya telah membuat algoritma integer-to-string tercepat. Ini adalah variasi dari algoritma Modulo 100 yang kira-kira 33% lebih cepat, dan yang terpenting lebih cepat untuk bilangan yang lebih kecil dan besar. Ini disebut Algoritma Script ItoS. Untuk membaca makalah yang menjelaskan bagaimana saya merekayasa algoritme @see https://github.com/kabuki-starship/kabuki-toolkit/wiki/Engineering-a-Faster-Integer-to-String-Algorithm . Anda dapat menggunakan algoritme tetapi harap pikirkan untuk berkontribusi kembali ke VM Kabuki dan periksa Script ; terutama jika Anda tertarik dengan AMIL-NLP dan / atau protokol jaringan yang ditentukan perangkat lunak.
Penulis
sumber
std::string
agar perbandingan dengan metode lain yang tercantum di sini valid. Pada awalnya saya tidak dapat memahami penggunaan operator shift untuk pohon pencarian biner, karena perbandingannya sudah sangat cepat, tetapi sekarang saya menyadari bahwa ini akan berguna untuk menghitung awal nilai yang bergeser jika Anda membutuhkannya. Anda tidak menggunakannya. Di sisi lain, Anda tidak berakhir dengan literal besar yang dikodekan di dalam instruksi, jadi mungkin itu alasan yang cukup.Modifikasi solusi user434507. Dimodifikasi untuk menggunakan larik karakter, bukan string C ++. Berjalan sedikit lebih cepat. Juga memindahkan cek 0 lebih rendah dalam kode ... karena ini tidak pernah terjadi untuk kasus khusus saya. Pindahkan kembali jika itu lebih umum untuk kasus Anda.
sumber
Mismatch found: Generated: -9999999990 Reference: -999999999 Mismatch found: Generated: -9999999980 Reference: -999999998 Mismatch found: Generated: -9999999970 Reference: -999999997
Kami menggunakan kode berikut (untuk MSVC):
Templated tBitScanReverse:
char / wchar_t pembantu:
Kekuatan 10 tabel:
Cetak aktual:
Loop terakhir dapat dibuka gulungannya:
Ide utamanya sama dengan @atlaste yang disarankan sebelumnya: https://stackoverflow.com/a/29039967/2204001
sumber
Baru saja menemukan ini karena aktivitas baru-baru ini; Saya tidak benar-benar punya waktu untuk menambahkan tolok ukur, tetapi saya ingin menambahkan apa yang saya tulis di masa lalu ketika saya membutuhkan integer cepat ke konversi string ...
https://github.com/CarloWood/ai-utils/blob/master/itoa.h
https://github.com/CarloWood/ai-utils/blob/master/itoa.cxx
Trik yang digunakan di sini adalah pengguna harus menyediakan std :: array yang cukup besar (di tumpukan mereka) dan kode ini menulis string ke belakang itu, dimulai dari unit, dan kemudian mengembalikan pointer ke dalam array dengan offset ke tempat hasil sebenarnya dimulai.
Oleh karena itu, ini tidak mengalokasikan atau memindahkan memori, tetapi masih memerlukan pembagian dan modulo per digit hasil (yang saya yakini cukup cepat karena itu hanya kode yang dijalankan secara internal pada CPU; akses memori biasanya menjadi masalah imho).
sumber
Mengapa tidak ada orang yang menggunakan fungsi div dari stdlib saat keduanya, hasil bagi dan sisa dibutuhkan?
Menggunakan kode sumber Timo, saya berakhir dengan sesuatu seperti ini:
Ok, untuk unsigned int, fungsi div tidak bisa digunakan tapi unsigned bisa ditangani secara terpisah.
Saya telah menetapkan makro COPYPAIR sebagai berikut untuk menguji variasi cara menyalin 2 karakter dari digit_pairs (tidak menemukan keuntungan yang jelas dari salah satu metode ini):
sumber