D は C++ と比べてどれくらい速いですか? 質問する

D は C++ と比べてどれくらい速いですか? 質問する

D のいくつかの機能は気に入っていますが、実行時にペナルティが発生する場合は興味がありますか?

比較のために、C++ と D の両方で多数の短いベクトルのスカラー積を計算する簡単なプログラムを実装しました。結果は驚くべきものでした。

  • D: 18.9 秒 [最終実行時間は下記を参照]
  • C++: 3.8 秒

C++ は本当に 5 倍近く高速なのでしょうか、それとも D プログラムでミスをしたのでしょうか?

私は、最近の Linux デスクトップで、C++ を g++ -O3 (gcc-snapshot 2011-02-19) でコンパイルし、D を dmd -O (dmd 2.052) でコンパイルしました。結果は複数回実行しても再現可能で、標準偏差は無視できるほど小さいです。

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

そしてこちらが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;
}

ベストアンサー1

すべての最適化を有効にし、すべての安全性チェックを無効にするには、次の DMD フラグを使用して D プログラムをコンパイルします。

-O -inline -release -noboundscheck

編集: g++、dmd、gdc でプログラムを試しました。dmd は確かに遅れていますが、gdc は g++ に非常に近いパフォーマンスを達成しています。私が使用したコマンドラインはgdmd -O -release -inline(gdmd は dmd オプションを受け入れる gdc のラッパーです) です。

アセンブラのリストを見ると、dmd も gdc もインライン化されていないようですscalar_productが、g++/gdc は MMX 命令を生成したので、ループを自動的にベクトル化している可能性があります。

おすすめ記事