Saya suka beberapa fitur D, tetapi akan tertarik jika mereka datang dengan penalti runtime?
Sebagai perbandingan, saya mengimplementasikan program sederhana yang menghitung produk skalar dari banyak vektor pendek baik dalam C ++ maupun dalam D. Hasilnya mengejutkan:
- D: 18,9 dt [lihat di bawah untuk runtime akhir]
- C ++: 3,8 dtk
Apakah C ++ benar-benar hampir lima kali lebih cepat atau apakah saya membuat kesalahan dalam program D.
Saya mengkompilasi C ++ dengan g ++ -O3 (gcc-snapshot 2011-02-19) dan D dengan dmd -O (dmd 2.052) pada desktop linux moderat terbaru. Hasilnya dapat direproduksi dalam beberapa proses dan standar deviasi dapat diabaikan.
Di sini program C ++:
#include <iostream>
#include <random>
#include <chrono>
#include <string>
#include <vector>
#include <array>
typedef std::chrono::duration<long, std::ratio<1, 1000>> millisecs;
template <typename _T>
long time_since(std::chrono::time_point<_T>& time) {
long tm = std::chrono::duration_cast<millisecs>( std::chrono::system_clock::now() - time).count();
time = std::chrono::system_clock::now();
return tm;
}
const long N = 20000;
const int size = 10;
typedef int value_type;
typedef long long result_type;
typedef std::vector<value_type> vector_t;
typedef typename vector_t::size_type size_type;
inline value_type scalar_product(const vector_t& x, const vector_t& y) {
value_type res = 0;
size_type siz = x.size();
for (size_type i = 0; i < siz; ++i)
res += x[i] * y[i];
return res;
}
int main() {
auto tm_before = std::chrono::system_clock::now();
// 1. allocate and fill randomly many short vectors
vector_t* xs = new vector_t [N];
for (int i = 0; i < N; ++i) {
xs[i] = vector_t(size);
}
std::cerr << "allocation: " << time_since(tm_before) << " ms" << std::endl;
std::mt19937 rnd_engine;
std::uniform_int_distribution<value_type> runif_gen(-1000, 1000);
for (int i = 0; i < N; ++i)
for (int j = 0; j < size; ++j)
xs[i][j] = runif_gen(rnd_engine);
std::cerr << "random generation: " << time_since(tm_before) << " ms" << std::endl;
// 2. compute all pairwise scalar products:
time_since(tm_before);
result_type avg = 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
avg += scalar_product(xs[i], xs[j]);
avg = avg / N*N;
auto time = time_since(tm_before);
std::cout << "result: " << avg << std::endl;
std::cout << "time: " << time << " ms" << std::endl;
}
Dan di sini versi D:
import std.stdio;
import std.datetime;
import std.random;
const long N = 20000;
const int size = 10;
alias int value_type;
alias long result_type;
alias value_type[] vector_t;
alias uint size_type;
value_type scalar_product(const ref vector_t x, const ref vector_t y) {
value_type res = 0;
size_type siz = x.length;
for (size_type i = 0; i < siz; ++i)
res += x[i] * y[i];
return res;
}
int main() {
auto tm_before = Clock.currTime();
// 1. allocate and fill randomly many short vectors
vector_t[] xs;
xs.length = N;
for (int i = 0; i < N; ++i) {
xs[i].length = size;
}
writefln("allocation: %i ", (Clock.currTime() - tm_before));
tm_before = Clock.currTime();
for (int i = 0; i < N; ++i)
for (int j = 0; j < size; ++j)
xs[i][j] = uniform(-1000, 1000);
writefln("random: %i ", (Clock.currTime() - tm_before));
tm_before = Clock.currTime();
// 2. compute all pairwise scalar products:
result_type avg = cast(result_type) 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
avg += scalar_product(xs[i], xs[j]);
avg = avg / N*N;
writefln("result: %d", avg);
auto time = Clock.currTime() - tm_before;
writefln("scalar products: %i ", time);
return 0;
}
c++
performance
runtime
d
Lars
sumber
sumber
avg = avg / N*N
(urutan operasi).dmd ... trace.def
saya dapaterror: unrecognized file extension def
. Dan dokumen dmd untuk optlink hanya menyebutkan Windows.Jawaban:
Untuk mengaktifkan semua optimasi dan menonaktifkan semua pemeriksaan keamanan, kompilasi program D Anda dengan flag DMD berikut:
EDIT : Saya sudah mencoba program Anda dengan g ++, dmd dan gdc. dmd memang ketinggalan, tetapi gdc mencapai kinerja yang sangat dekat dengan g ++. Baris perintah yang saya gunakan adalah
gdmd -O -release -inline
(gdmd adalah pembungkus di sekitar gdc yang menerima opsi dmd).Melihat daftar assembler, sepertinya dmd atau gdc tidak bergaris
scalar_product
, tetapi g ++ / gdc memang memancarkan instruksi MMX, jadi mereka mungkin secara otomatis meng-vektor-kan loop.sumber
Satu hal besar yang memperlambat D down adalah implementasi pengumpulan sampah di bawah standar. Benchmark yang tidak terlalu menekankan GC akan menunjukkan kinerja yang sangat mirip dengan kode C dan C ++ yang dikompilasi dengan backend kompiler yang sama. Benchmark yang sangat menekankan GC akan menunjukkan bahwa D berkinerja buruk. Namun, yakinlah, ini adalah masalah implementasi-kualitas tunggal (meskipun parah), bukan jaminan kelambatan. Juga, D memberi Anda kemampuan untuk keluar dari GC dan menyelaraskan manajemen memori dalam bit-bit yang kritis terhadap kinerja, sambil tetap menggunakannya dalam 95% kode Anda yang kurang kritis terhadap kinerja.
Saya telah berupaya meningkatkan kinerja GC belakangan ini dan hasilnya agak dramatis, setidaknya pada tolok ukur sintetis. Semoga perubahan ini akan diintegrasikan ke dalam salah satu dari beberapa rilis berikutnya dan akan mengurangi masalah ini.
sumber
Ini adalah utas yang sangat instruktif, terima kasih atas semua kerja untuk OP dan para pembantunya.
Satu catatan - tes ini tidak menilai pertanyaan umum abstraksi / penalti fitur atau bahkan kualitas backend. Ini berfokus pada hampir satu optimasi (optimasi lingkaran). Saya pikir itu adil untuk mengatakan bahwa backend gcc agak lebih halus daripada dmd, tetapi itu akan menjadi kesalahan untuk mengasumsikan bahwa kesenjangan di antara mereka sama besar untuk semua tugas.
sumber
Jelas tampak seperti masalah kualitas implementasi.
Saya menjalankan beberapa tes dengan kode OP dan membuat beberapa perubahan. Saya benar-benar mendapatkan D lebih cepat untuk LDC / clang ++, beroperasi dengan asumsi bahwa array harus dialokasikan secara dinamis (
xs
dan skalar terkait). Lihat di bawah untuk beberapa angka.Pertanyaan untuk OP
Apakah disengaja bahwa benih yang sama digunakan untuk setiap iterasi C ++, sementara tidak demikian untuk D?
Mempersiapkan
Saya telah men-tweak sumber D asli (dijuluki
scalar.d
) untuk membuatnya portabel antar platform. Ini hanya melibatkan mengubah jenis angka yang digunakan untuk mengakses dan memodifikasi ukuran array.Setelah ini, saya membuat perubahan berikut:
Digunakan
uninitializedArray
untuk menghindari init default untuk skalar di xs (mungkin membuat perbedaan terbesar). Ini penting karena D biasanya menginisialisasi semuanya diam-diam, yang tidak C ++.Diperhatikan kode pencetakan dan diganti
writefln
denganwriteln
^^
) alih-alih penggandaan manual untuk langkah akhir penghitungan rata-ratasize_type
dan diganti dengan tepat denganindex_type
alias baru... sehingga menghasilkan
scalar2.cpp
( pastebin ):Setelah pengujian
scalar2.d
(yang memprioritaskan optimasi untuk kecepatan), karena penasaran saya mengganti loopmain
dengan yangforeach
setara, dan menyebutnyascalar3.d
( pastebin ):Saya mengkompilasi masing-masing tes ini menggunakan kompiler berbasis LLVM, karena LDC tampaknya menjadi pilihan terbaik untuk kompilasi D dalam hal kinerja. Pada instalasi Arch Linux x86_64 saya, saya menggunakan paket-paket berikut:
clang 3.6.0-3
ldc 1:0.15.1-4
dtools 2.067.0-2
Saya menggunakan perintah berikut untuk mengkompilasi masing-masing:
clang++ scalar.cpp -o"scalar.cpp.exe" -std=c++11 -O3
rdmd --compiler=ldc2 -O3 -boundscheck=off <sourcefile>
Hasil
Hasil ( tangkapan layar output konsol mentah ) dari setiap versi sumber sebagai berikut:
scalar.cpp
(C ++ asli):C ++ menetapkan standar pada 2582 ms .
scalar.d
(sumber OP yang dimodifikasi):Ini berjalan selama ~ 2957 ms . Lebih lambat dari implementasi C ++, tetapi tidak terlalu banyak.
scalar2.d
(indeks / perubahan jenis panjang dan optimasi Array tidak diinisialisasi):Dengan kata lain, ~ 1860 ms . Sejauh ini memimpin.
scalar3.d
(dahi):~ 2182 ms lebih lambat daripada
scalar2.d
, tetapi lebih cepat dari versi C ++.Kesimpulan
Dengan optimasi yang benar, implementasi D sebenarnya berjalan lebih cepat daripada implementasi C ++ yang setara menggunakan kompiler berbasis LLVM yang tersedia. Kesenjangan saat ini antara D dan C ++ untuk sebagian besar aplikasi tampaknya hanya didasarkan pada batasan implementasi saat ini.
sumber
dmd adalah implementasi referensi bahasa dan dengan demikian sebagian besar pekerjaan diletakkan di frontend untuk memperbaiki bug daripada mengoptimalkan backend.
"in" lebih cepat dalam kasus Anda karena Anda menggunakan array dinamis yang merupakan tipe referensi. Dengan ref Anda memperkenalkan tingkat tipuan yang lain (yang biasanya digunakan untuk mengubah array itu sendiri dan tidak hanya konten).
Vektor biasanya diimplementasikan dengan struct di mana const ref masuk akal. Lihat smallptD vs smallpt untuk contoh dunia nyata yang menampilkan banyak operasi vektor dan keacakan.
Perhatikan bahwa 64-Bit juga dapat membuat perbedaan. Saya pernah melewatkan bahwa pada x64 gcc mengkompilasi kode 64-Bit sementara dmd masih default ke 32 (akan berubah ketika codegen 64-Bit matang). Ada speedup luar biasa dengan "dmd -m64 ...".
sumber
Apakah C ++ atau D lebih cepat cenderung sangat tergantung pada apa yang Anda lakukan. Saya akan berpikir bahwa ketika membandingkan C ++ yang ditulis dengan baik dengan kode D yang ditulis dengan baik, mereka umumnya akan memiliki kecepatan yang sama, atau C ++ akan lebih cepat, tetapi apa yang dikelola oleh kompiler tertentu untuk dioptimalkan dapat memiliki efek besar sepenuhnya selain dari bahasa diri.
Namun, ada yang beberapa kasus di mana D berdiri sebuah kesempatan baik untuk mengalahkan C ++ untuk kecepatan. Yang utama yang terlintas dalam pikiran adalah pemrosesan string. Berkat kemampuan mengiris array D, string (dan array secara umum) dapat diproses lebih cepat daripada yang dapat Anda lakukan dengan mudah di C ++. Untuk D1, prosesor XML Tango sangat cepat , terutama berkat kemampuan mengiris array D (dan mudah-mudahan D2 akan memiliki parser XML yang sama cepat setelah yang sedang dikerjakan untuk Phobos telah selesai). Jadi, pada akhirnya apakah D atau C ++ akan menjadi lebih cepat akan sangat tergantung pada apa yang Anda lakukan.
Sekarang, saya sedang terkejut bahwa Anda melihat perbedaan dalam kecepatan dalam kasus ini, tetapi itu adalah semacam hal yang saya harapkan untuk meningkatkan sebagai DMD membaik. Menggunakan gdc mungkin menghasilkan hasil yang lebih baik dan kemungkinan akan menjadi perbandingan yang lebih dekat dari bahasa itu sendiri (bukan backend) mengingat bahwa itu berbasis gcc. Tapi itu tidak akan mengejutkan saya sama sekali jika ada beberapa hal yang dapat dilakukan untuk mempercepat kode yang dmd hasilkan. Saya tidak berpikir bahwa ada banyak pertanyaan bahwa gcc lebih dewasa daripada dmd pada saat ini. Dan optimasi kode adalah salah satu buah utama dari kematangan kode.
Pada akhirnya, yang penting adalah seberapa baik dmd berkinerja untuk aplikasi khusus Anda, tetapi saya setuju bahwa pasti akan menyenangkan untuk mengetahui seberapa baik C ++ dan D dibandingkan secara umum. Secara teori, mereka harusnya hampir sama, tetapi itu benar-benar tergantung pada implementasinya. Saya pikir satu set tolok ukur komprehensif akan diperlukan untuk benar-benar menguji seberapa baik keduanya membandingkan saat ini.
sumber
Anda dapat menulis kode C sejauh D yang lebih cepat, itu akan tergantung pada banyak hal:
Perbedaan yang pertama tidak adil untuk diseret. Yang kedua mungkin memberi C ++ keuntungan karena, jika ada, memiliki fitur berat yang lebih sedikit. Yang ketiga adalah yang menyenangkan: kode D dalam beberapa hal lebih mudah dioptimalkan karena secara umum lebih mudah dipahami. Juga memiliki kemampuan untuk melakukan program generatif tingkat besar yang memungkinkan hal-hal seperti verbose dan kode berulang tetapi cepat ditulis dalam bentuk yang lebih pendek.
sumber
Sepertinya kualitas masalah implementasi. Misalnya, inilah yang telah saya uji:
Dengan
ManualInline
didefinisikan saya mendapatkan 28 detik, tetapi tanpa saya mendapatkan 32. Jadi kompiler bahkan tidak menggarisbawahi fungsi sederhana ini, yang saya pikir sudah jelas seharusnya.(Baris perintah saya adalah
dmd -O -noboundscheck -inline -release ...
.)sumber