সি ++ এর তুলনায় ডি কত দ্রুত?


133

আমি ডি এর কিছু বৈশিষ্ট্য পছন্দ করি তবে তারা যদি রানটাইম পেনাল্টি নিয়ে আসে তবে আগ্রহী হবে?

তুলনা করার জন্য, আমি একটি সাধারণ প্রোগ্রাম বাস্তবায়ন করেছি যা সি ++ এবং ডি উভয় ক্ষেত্রে অনেক সংক্ষিপ্ত ভেক্টরের স্কেলার পণ্যগুলিকে গণনা করে ফলাফলটি অবাক করার মতো:

  • ডি: 18.9 এস [চূড়ান্ত রানটাইমের জন্য নীচে দেখুন]
  • সি ++: 3.8 এস

সি ++ আসলেই প্রায় পাঁচগুণ দ্রুত বা আমি ডি প্রোগ্রামটিতে ভুল করেছি?

আমি জি ++ -O3 (জিসিসি-স্ন্যাপশট 2011-02-19) এবং ডিএমডি-ও (ডিএমডি 2.052) সহ একটি মধ্যম সাম্প্রতিক লিনাক্স ডেস্কটপে সি ++ সংকলন করেছি। ফলাফলগুলি বেশ কয়েকটি রান এবং মানক বিচ্যুতির তুলনায় পুনরুত্পাদনযোগ্য l

এখানে সি ++ প্রোগ্রাম:

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

এবং এখানে ডি সংস্করণ:

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

3
যাইহোক, আপনার প্রোগ্রামটির এই লাইনে একটি বাগ রয়েছে: avg = avg / N*N(ক্রিয়াকলাপের ক্রম)।
ভ্লাদিমির পানতেলিভ

4
আপনি অ্যারে / ভেক্টর অপারেশনগুলি ডিজিটালমার্স.com
মাইকেল মিনিচ

10
আরও ভাল তুলনা দেওয়ার জন্য আপনার একই সংকলকটি ব্যাক-এন্ড ব্যবহার করা উচিত। হয় ডিএমডি এবং ডিএমসি ++ বা জিডিসি এবং জি ++
তিনি_গ্রেট

1
@ সায়ন শেভোক দুর্ভাগ্যক্রমে, ডিএমডি প্রোফাইলিং কি লিনাক্সের জন্য উপলব্ধ বলে মনে হচ্ছে না? (দয়া করে আমি ভুল হলে আমাকে সংশোধন করুন, তবে আমি যদি বলি যে dmd ... trace.defআমি একটি error: unrecognized file extension defপেয়েছি opt এবং অপ্টলিংকের জন্য ডিএমডি ডক্সটিতে কেবল উইন্ডোজ উল্লেখ করা হয়েছে
লার্স

1
আহ, কখনই। .ডিফ ফাইলটির বাইরে যায় না। সময়গুলি। লগ ফাইলের অভ্যন্তরে। "এতে লিঙ্কারটি তাদের ক্রম অনুসারে ক্রিয়াকলাপগুলির তালিকা অন্তর্ভুক্ত করে" - এটি কোনও কিছুর অনুকূলিতকরণ করতে অপ্টলিংকে সহায়তা করে? এছাড়াও নোট করুন যে "অতিরিক্ত হিসাবে, এলডি স্ট্যান্ডার্ড" * .ডিএফ "ফাইলগুলিকে সম্পূর্ণ সমর্থন করে, যা লিঙ্কার কমান্ড লাইনে কোনও অবজেক্ট ফাইলের মতো নির্দিষ্ট করা যেতে পারে" - সুতরাং আপনি যদি খুব পছন্দ করেন তবে আপনি ট্রেস.ডেফ পাস করার চেষ্টা করতে পারেন - প্রতি.
Trass3r

উত্তর:


64

সমস্ত অপ্টিমাইজেশান সক্ষম করতে এবং সমস্ত সুরক্ষা চেক অক্ষম করতে, নিম্নলিখিত ডিএমডি পতাকা সহ আপনার ডি প্রোগ্রামটি সংকলন করুন:

-O -inline -release -noboundscheck

সম্পাদনা : আমি আপনার প্রোগ্রামগুলি জি ++, ডিএমডি এবং জিডিসি দিয়ে চেষ্টা করেছি। ডিএমডি পিছনে পড়ে না, তবে জিডিসি জি ++ এর খুব কাছাকাছি পারফরম্যান্স অর্জন করে। আমি যে gdmd -O -release -inlineকমান্ডলাইনটি ব্যবহার করেছি তা হ'ল (জিডিএমডি হ'ল জিডিসি-র একটি মোড়ক যা ডিএমডি বিকল্পগুলি গ্রহণ করে)।

এসেম্বলারের তালিকাটি দেখে মনে হচ্ছে এটি ডিএমডি বা জিডিসি অন্তর্ভুক্ত নয় scalar_product, তবে জি ++ / জিডিসি এমএমএক্স নির্দেশাবলী নির্গত করে, তাই তারা লুপটি স্বয়ংক্রিয়ভাবে ভেক্টরাইজিং করতে পারে।


3
@ সাইবারশ্যাডো: তবে আপনি যদি সুরক্ষা চেকটি সরিয়ে দেন ... আপনি ডি এর কিছু গুরুত্বপূর্ণ বৈশিষ্ট্য হারাচ্ছেন না?
ম্যাথিউ এম।

33
আপনি এমন বৈশিষ্ট্যগুলি হারাচ্ছেন যা সি ++ এর আগে কখনও ছিল না। বেশিরভাগ ভাষা আপনাকে পছন্দ দেয় না।
ভ্লাদিমির পানতেলিভ

6
@ সাইবারশ্যাডো: আমরা কি এটিকে ডিবাগ বনাম রিলিজ বিল্ড হিসাবে ভাবতে পারি?
ফ্রান্সেস্কো

7
@ বার্নার্ড: ইন-রিরিজ, সিকিউরিড ফাংশন ব্যতীত সকল কোডের জন্য সীমা পরীক্ষা করা বন্ধ রয়েছে। সত্যিকার অর্থে চেক অফ সীমা বন্ধ করতে - রিলেজ এবং-নোবাউন্ডশেক উভয়ই ব্যবহার করুন।
মিশাল মিনিচ

5
@ সাইবারশেডো ধন্যবাদ! এই পতাকাগুলির সাথে রানটাইম যথেষ্ট উন্নতি করে। এখন ডি 12.9 এস এ। তবে এখনও দীর্ঘ 3 বারের বেশি চালায়। @ ম্যাথিউ এম। আমি ধীর গতিতে বাউন্ডসেকিং সহ একটি প্রোগ্রাম পরীক্ষা করতে কিছু মনে করব না এবং এটি একবার ডিবাগ হয়ে গেলে এটি বাউন্ডসেকিং ছাড়াই এর গণনাগুলি করতে দেয়। (আমি এখন সি ++ দিয়েও একই কাজ করি))
লার্স

32

ডিটি হ্রাস করে এমন একটি বড় জিনিস হ'ল একটি সাব্পার আবর্জনা সংগ্রহের বাস্তবায়ন। জেনারেলগুলি গুরুতরভাবে চাপ দেয় না এমন মানদণ্ডগুলি একই সংকলক ব্যাকএন্ডের সাথে সংকলিত সি এবং সি ++ কোডের সাথে খুব অনুরূপ পারফরম্যান্স দেখায়। জিসিকে প্রচন্ড চাপ দেয় এমন মানদণ্ডগুলি দেখিয়ে দেবে যে ডি দুর্দান্তভাবে অভিনয় করে। আশ্বাস দিন, যদিও, এটি বাস্তবায়নের একক (তীব্র হলেও) গুণমানের সমস্যা, অলসতার কোনও বেকড ইন গ্যারান্টি নয়। এছাড়াও, ডি আপনাকে জি.সি এবং অপারেশন-সমালোচনামূলক বিটগুলিতে মেমরি পরিচালনা থেকে বেরিয়ে আসার ক্ষমতা দেয় এবং এখনও এটি আপনার কোডের 95% কম পারফরম্যান্সে ব্যবহার করে।

আমি ইদানীং জিসি পারফরম্যান্সের উন্নতির জন্য কিছু প্রচেষ্টা করেছি এবং ফলাফলগুলি নাটকীয় হয়ে উঠেছে, কমপক্ষে সিন্থেটিক মানদণ্ডে। আশা করি এই পরিবর্তনগুলি পরবর্তী কয়েকটি প্রকাশের একটিতে সংহত হবে এবং সমস্যাটি প্রশমিত করবে।


1
আমি লক্ষ্য করেছি যে আপনার একটি পরিবর্তন বিভাগ থেকে বিট শিফটে পরিবর্তন। যে সংকলক কিছু করা উচিত নয়?
GManNickG

3
@ জিএমান: হ্যাঁ, আপনি যে মানটি ভাগ করছেন তা সংকলনের সময়টি যদি জানা থাকে। না, যদি মানটি কেবল রানটাইমের সময় জানা থাকে তবে আমি সেই অপটিমাইজেশনটি তৈরি করেছি।
dsimcha

@ ডিএসিমচা: এইচএম আমি মনে করি আপনি এটি তৈরি করতে জানেন, সংকলকটিও করতে পারে। বাস্তবায়নের সমস্যার গুণমান, বা আমি অনুভব করছি যে সংস্থাপক প্রমাণ করতে পারে না এমন কোনও শর্তের সন্তুষ্ট হওয়া দরকার, তবে আপনি কি জানেন? (আমি এখন ডি শিখছি, তাই সংকলক সম্পর্কে এই ছোট্ট জিনিসগুলি হঠাৎই আমার কাছে আকর্ষণীয় :) :))
GManNickG

13
@ জিএমএন: বিট স্থানান্তর কেবল তখনই কাজ করে যদি আপনি যে সংখ্যাটি বিভাজক করছেন তার সংখ্যা দুটি হয়। সংকলক এটি প্রমাণ করতে পারে না যদি সংখ্যাটি কেবল রানটাইম অনুসারে পরিচিত হয়, এবং পরীক্ষার এবং শাখা প্রশাখাগুলি কেবল ডিভি নির্দেশ ব্যবহার না করে ধীর হয়ে যায়। আমার ক্ষেত্রে অস্বাভাবিক কারণ মানটি কেবল রানটাইমের সময়ই পরিচিত তবে আমি সংকলনের সময় জানি যে এটি দু'জনের কিছুটা শক্তি হতে চলেছে।
dsimcha

7
নোট করুন যে এই উদাহরণটিতে পোস্ট করা প্রোগ্রাম সময়সাপেক্ষ অংশে বরাদ্দ দেয় না।
ভ্লাদিমির পানতেলিভ

27

এটি ওপি এবং সহায়কদের সমস্ত কাজের জন্য ধন্যবাদ একটি খুব শিক্ষামূলক থ্রেড।

একটি নোট - এই পরীক্ষা বিমূর্ততা / বৈশিষ্ট্য জরিমানা বা এমনকি ব্যাকএন্ডের মানের যে সাধারণ প্রশ্নকে মূল্যায়ন করছে না। এটি কার্যত একটি অপ্টিমাইজেশন (লুপ অপ্টিমাইজেশন) উপর দৃষ্টি নিবদ্ধ করে। আমি মনে করি এটি যথাযথভাবে মনে হয় যে সিসির ব্যাকএন্ডটি ডিএমডির চেয়ে কিছুটা বেশি পরিশ্রুত, তবে এটি অনুমান করা ভুল হবে যে সমস্ত কাজের ক্ষেত্রে তাদের মধ্যে ব্যবধানটি ততটাই বিশাল।


4
আমি পুরোপুরি একমত. পরে যেমন যোগ করা হয়েছে, আমি মূলত সংখ্যামূলক গণনাগুলির জন্য পারফরম্যান্সে আগ্রহী যেখানে লুপ অপ্টিমাইজেশন সম্ভবত সবচেয়ে গুরুত্বপূর্ণ। সংখ্যার কম্পিউটিংয়ের জন্য অন্য কোনটি অপ্টিমাইজেশান গুরুত্বপূর্ণ বলে মনে করেন? এবং কোন গণনা তাদের পরীক্ষা করবে? আমি আমার পরীক্ষার পরিপূরক করতে এবং আরও কিছু পরীক্ষার বাস্তবায়ন করতে আগ্রহী (যদি সেগুলি প্রায় সহজ হয়)। কিন্তু স্পষ্টত এটি তার নিজের আরেকটি প্রশ্ন?
লার্স

11
ইঞ্জিনিয়ার হিসাবে যে সি ++ তে দাঁত কাটল, আপনি আমার একজন নায়ক। সম্মানজনকভাবে, তবে এটি একটি মন্তব্য হওয়া উচিত, উত্তর নয়।
অ্যালান

14

অবশ্যই বাস্তবায়ন মানের মানের মনে হচ্ছে।

আমি ওপির কোড দিয়ে কিছু পরীক্ষা চালিয়েছি এবং কিছু পরিবর্তন করেছি। আমি আসলে এলডিসি / ঝনঝন ++ এর জন্য দ্রুত গতিতে চলে এসেছি , এই ধারনাটি চালিয়ে যে অ্যারেগুলি অবশ্যই গতিশীলভাবে বরাদ্দ করা উচিত ( xsএবং সম্পর্কিত স্কেলারগুলি)। কিছু সংখ্যার জন্য নীচে দেখুন।

ওপি জন্য প্রশ্ন

এটি ইচ্ছাকৃত যে বীজটি সি ++ এর প্রতিটি পুনরাবৃত্তির জন্য ব্যবহার করা হবে, তবে ডি এর ক্ষেত্রে তা নয়?

সেটআপ

scalar.dপ্ল্যাটফর্মগুলির মধ্যে পোর্টেবল করার জন্য আমি মূল ডি উত্সটি (ডাবড ) টুইট করেছি। এটি কেবল অ্যারের আকার অ্যাক্সেস এবং সংশোধন করতে ব্যবহৃত সংখ্যার ধরণ পরিবর্তন করার সাথে জড়িত।

এর পরে, আমি নিম্নলিখিত পরিবর্তনগুলি করেছি:

  • uninitializedArrayএক্স-এর স্কেলারের জন্য ডিফল্ট ইনট এড়ানোর জন্য ব্যবহৃত (সম্ভবত সবচেয়ে বড় পার্থক্য হয়েছে)। এটি গুরুত্বপূর্ণ কারণ ডি সাধারনত নিঃশব্দে সমস্ত কিছু ডিফল্ট করে দেয়, যা সি ++ দেয় না।

  • মুদ্রণ কোডটি ফ্যাক্টর করেছে এবং এর writeflnসাথে প্রতিস্থাপিত হয়েছেwriteln

  • পরিবর্তিত আমদানি নির্বাচনী হতে হবে
  • ^^গড় গণনার চূড়ান্ত পদক্ষেপের জন্য ম্যানুয়াল গুণনের পরিবর্তে পাউ অপারেটর ( ) ব্যবহার করুন
  • মুছে ফেলা হয়েছে size_typeএবং নতুন index_typeউপন্যাসের সাথে যথাযথ প্রতিস্থাপন করা হয়েছে

... এভাবে scalar2.cpp( পেস্টবিন ) ফলাফল:

    import std.stdio : writeln;
    import std.datetime : Clock, Duration;
    import std.array : uninitializedArray;
    import std.random : uniform;

    alias result_type = long;
    alias value_type = int;
    alias vector_t = value_type[];
    alias index_type = typeof(vector_t.init.length);// Make index integrals portable - Linux is ulong, Win8.1 is uint

    immutable long N = 20000;
    immutable int size = 10;

    // Replaced for loops with appropriate foreach versions
    value_type scalar_product(in ref vector_t x, in ref vector_t y) { // "in" is the same as "const" here
      value_type res = 0;
      for(index_type i = 0; i < size; ++i)
        res += x[i] * y[i];
      return res;
    }

    int main() {
      auto tm_before = Clock.currTime;
      auto countElapsed(in string taskName) { // Factor out printing code
        writeln(taskName, ": ", Clock.currTime - tm_before);
        tm_before = Clock.currTime;
      }

      // 1. allocate and fill randomly many short vectors
      vector_t[] xs = uninitializedArray!(vector_t[])(N);// Avoid default inits of inner arrays
      for(index_type i = 0; i < N; ++i)
        xs[i] = uninitializedArray!(vector_t)(size);// Avoid more default inits of values
      countElapsed("allocation");

      for(index_type i = 0; i < N; ++i)
        for(index_type j = 0; j < size; ++j)
          xs[i][j] = uniform(-1000, 1000);
      countElapsed("random");

      // 2. compute all pairwise scalar products:
      result_type avg = 0;
      for(index_type i = 0; i < N; ++i)
        for(index_type j = 0; j < N; ++j)
          avg += scalar_product(xs[i], xs[j]);
      avg /= N ^^ 2;// Replace manual multiplication with pow operator
      writeln("result: ", avg);
      countElapsed("scalar products");

      return 0;
    }

পরীক্ষার পরে scalar2.d(যা গতির জন্য অপ্টিমাইজেশনকে প্রাধান্য দিয়েছিল), কৌতূহলের বাইরে আমি লুপগুলি সমতুল্য mainসাথে প্রতিস্থাপন করেছি foreachএবং এটিকে scalar3.d( পেস্টবিন ) ডেকেছি :

    import std.stdio : writeln;
    import std.datetime : Clock, Duration;
    import std.array : uninitializedArray;
    import std.random : uniform;

    alias result_type = long;
    alias value_type = int;
    alias vector_t = value_type[];
    alias index_type = typeof(vector_t.init.length);// Make index integrals portable - Linux is ulong, Win8.1 is uint

    immutable long N = 20000;
    immutable int size = 10;

    // Replaced for loops with appropriate foreach versions
    value_type scalar_product(in ref vector_t x, in ref vector_t y) { // "in" is the same as "const" here
      value_type res = 0;
      for(index_type i = 0; i < size; ++i)
        res += x[i] * y[i];
      return res;
    }

    int main() {
      auto tm_before = Clock.currTime;
      auto countElapsed(in string taskName) { // Factor out printing code
        writeln(taskName, ": ", Clock.currTime - tm_before);
        tm_before = Clock.currTime;
      }

      // 1. allocate and fill randomly many short vectors
      vector_t[] xs = uninitializedArray!(vector_t[])(N);// Avoid default inits of inner arrays
      foreach(ref x; xs)
        x = uninitializedArray!(vector_t)(size);// Avoid more default inits of values
      countElapsed("allocation");

      foreach(ref x; xs)
        foreach(ref val; x)
          val = uniform(-1000, 1000);
      countElapsed("random");

      // 2. compute all pairwise scalar products:
      result_type avg = 0;
      foreach(const ref x; xs)
        foreach(const ref y; xs)
          avg += scalar_product(x, y);
      avg /= N ^^ 2;// Replace manual multiplication with pow operator
      writeln("result: ", avg);
      countElapsed("scalar products");

      return 0;
    }

আমি এই পরীক্ষাগুলির প্রত্যেকটি একটি এলএলভিএম ভিত্তিক সংকলক ব্যবহার করে সংকলন করেছি, যেহেতু এলডিসি পারফরম্যান্সের ক্ষেত্রে ডি সংকলনের জন্য সেরা বিকল্প বলে মনে হচ্ছে। আমার x86_64 আর্ক লিনাক্স ইনস্টলেশনতে আমি নিম্নলিখিত প্যাকেজগুলি ব্যবহার করেছি:

  • clang 3.6.0-3
  • ldc 1:0.15.1-4
  • dtools 2.067.0-2

প্রত্যেকটি সংকলনের জন্য আমি নিম্নলিখিত কমান্ডগুলি ব্যবহার করেছি:

  • সি ++: clang++ scalar.cpp -o"scalar.cpp.exe" -std=c++11 -O3
  • ডি: rdmd --compiler=ldc2 -O3 -boundscheck=off <sourcefile>

ফলাফল

উত্সের প্রতিটি সংস্করণের ফলাফল ( কাঁচা কনসোল আউটপুট এর স্ক্রিনশট ) নীচে হিসাবে রয়েছে:

  1. scalar.cpp (মূল সি ++):

    allocation: 2 ms
    
    random generation: 12 ms
    
    result: 29248300000
    
    time: 2582 ms

    সি ++ 2582 এমএস এ মান নির্ধারণ করে ।

  2. scalar.d (পরিবর্তিত ওপি উত্স):

    allocation: 5 ms, 293 μs, and 5 hnsecs 
    
    random: 10 ms, 866 μs, and 4 hnsecs 
    
    result: 53237080000
    
    scalar products: 2 secs, 956 ms, 513 μs, and 7 hnsecs 

    এটি 5 2957 এমএসে চলেছে । সি ++ প্রয়োগের চেয়ে ধীর, তবে খুব বেশি নয়।

  3. scalar2.d (সূচক / দৈর্ঘ্যের ধরণের পরিবর্তন এবং আনইনটিয়ালাইজড অ্যারে অপ্টিমাইজেশন):

    allocation: 2 ms, 464 μs, and 2 hnsecs
    
    random: 5 ms, 792 μs, and 6 hnsecs
    
    result: 59
    
    scalar products: 1 sec, 859 ms, 942 μs, and 9 hnsecs

    অন্য কথায়, 60 1860 এমএস । এখনও পর্যন্ত এটি নেতৃত্বে।

  4. scalar3.d (Foreaches):

    allocation: 2 ms, 911 μs, and 3 hnsecs
    
    random: 7 ms, 567 μs, and 8 hnsecs
    
    result: 189
    
    scalar products: 2 secs, 182 ms, and 366 μs

    82 2182 এমএসের চেয়ে ধীর গতি scalar2.d, তবে সি ++ সংস্করণের চেয়ে দ্রুত।

উপসংহার

সঠিক অপ্টিমাইজেশন সহ, ডি এল বাস্তবায়ন উপলব্ধ এলএলভিএম ভিত্তিক সংকলকগুলি ব্যবহার করে এর সমতুল্য সি ++ প্রয়োগকরণের চেয়ে দ্রুত গতিতে চলেছে। বেশিরভাগ অ্যাপ্লিকেশনগুলির জন্য ডি এবং সি ++ এর মধ্যে বর্তমান ব্যবধানটি কেবলমাত্র বাস্তবায়নগুলির সীমাবদ্ধতার ভিত্তিতে বলে মনে হচ্ছে।


8

ডিএমডি হ'ল ভাষার রেফারেন্স বাস্তবায়ন এবং সুতরাং বেশিরভাগ কাজ ব্যাকএন্ডকে অনুকূলিত করার পরিবর্তে বাগগুলি ঠিক করার জন্য সীমান্তে রেখে দেওয়া হয়।

আপনার ক্ষেত্রে "ইন" দ্রুততর হওয়ার কারণে আপনি গতিশীল অ্যারেগুলি ব্যবহার করছেন যা রেফারেন্স ধরণের। রেফের সাহায্যে আপনি ইন্ডিয়ারেশনের আরও একটি স্তর (যা সাধারণত অ্যারে নিজেই পরিবর্তিত করতে ব্যবহৃত হয় কেবল বিষয়বস্তুগুলিতেই ব্যবহৃত হয় না)।

ভেক্টরগুলি সাধারণত স্ট্রাক্ট দিয়ে প্রয়োগ করা হয় যেখানে কনস্ট রেফ নিখুঁত ধারণা দেয়। ভেক্টর ক্রিয়াকলাপ এবং এলোমেলোতা বোঝায় এমন একটি বাস্তব-বিশ্বের উদাহরণের জন্য স্মার্টপট বনাম ছোট্ট দেখুন ।

নোট করুন যে -৪-বিট এছাড়াও একটি পার্থক্য করতে পারে। আমি একবার মিস করেছি যে x64 গিসি তে 64৪-বিট কোডটি কম্পাইল করে যখন ডিএমডি এখনও ডিফল্ট 32-এ (যখন 64-বিট কোডজেন পরিপক্ক হবে তখন পরিবর্তন হবে)। "Dmd -m64 ..." সহ একটি অসাধারণ গতি ছিল।


7

সি ++ বা ডি দ্রুততর কিনা আপনি যা করছেন তার উপর নির্ভরশীল হতে পারে। আমি মনে করি যে ভালভাবে লিখিত সি ++ টি ভাল-রচিত ডি কোডের সাথে তুলনা করার সময় সেগুলি সাধারণত হয় একই গতিতে বা সি ++ আরও দ্রুত হত তবে নির্দিষ্ট সংকলকটি অনুকূলিতকরণের জন্য যা ভাষা থেকে পুরোপুরি আলাদা করে রাখতে পারে তার একটি বড় প্রভাব থাকতে পারে নিজেই।

যাইহোক, হয় কয়েক ক্ষেত্রে যেখানে ডি গতির জন্য সি ++ প্রহার একটি ভাল সুযোগ দাঁড়িয়েছে। মূল যেটি মনে আসে তা হল স্ট্রিং প্রসেসিং। ডি এর অ্যারে স্লাইসিং ক্যাপাবিলিটিসটির জন্য ধন্যবাদ ডি 1 এর জন্য, ট্যাঙ্গোর এক্সএমএল প্রসেসর অত্যন্ত দ্রুত , মূলত ডি এর অ্যারে স্লাইসিং ক্ষমতার জন্য ধন্যবাদ (এবং আশা করি ফোবোসের জন্য বর্তমানে কাজ করা কাজটি শেষ হওয়ার পরে ডি 2 তে একইভাবে দ্রুত এক্সএমএল পার্সার পাবেন)। সুতরাং, শেষ পর্যন্ত ডি বা সি ++ দ্রুত হতে চলেছে কিনা আপনি যা করছেন তার উপর খুব নির্ভরশীল হতে চলেছে।

এখন, আমি অবাক হয়েছি যে আপনি এই বিশেষ ক্ষেত্রে গতির মধ্যে এতটা পার্থক্য দেখছেন, তবে এটি এমন এক ধরণের জিনিস যা আমি dmd এর উন্নতির সাথে সাথে উন্নতির আশা করব। জিডিসি ব্যবহারের ফলে আরও ভাল ফলাফল হতে পারে এবং এটি জিসিসি ভিত্তিক ভিত্তিতে ভাষার সাথে (ব্যাকএন্ডের চেয়ে বরং) আরও ঘনিষ্ঠ তুলনা হতে পারে। Dmd কোড উত্পন্ন করে যা কোড গতিতে করতে পারে এমন অনেকগুলি জিনিস থাকলে এটি আমাকে মোটেই অবাক করে না। আমি মনে করি না যে এই মুহুর্তে gmc dmd এর চেয়ে বেশি পরিপক্ক question কোড অপূর্ণতা কোড পরিপক্কতার অন্যতম প্রধান ফল।

শেষ পর্যন্ত, ডিএমডি আপনার নির্দিষ্ট অ্যাপ্লিকেশনের জন্য কতটা ভাল পারফর্ম করে তা গুরুত্বপূর্ণ, তবে আমি সম্মত হই যে সি ++ এবং ডি সাধারণভাবে কতটা ভাল তুলনা করে তা জানতে পেরে অবশ্যই চমৎকার লাগবে। তাত্ত্বিকভাবে, তাদের বেশ একই রকম হওয়া উচিত, তবে এটি বাস্তবায়নের উপর নির্ভর করে। আমি মনে করি যে দুটি বর্তমানে বর্তমানে তুলনামূলকভাবে কতটা ভাল তুলনা করতে হবে তা পরীক্ষা করার জন্য একটি বেঞ্চমার্কের একটি বিস্তৃত সেট প্রয়োজন।


4
হ্যাঁ আমি আশ্চর্য হব যদি ইনপুট / আউটপুট উভয় ভাষায় উল্লেখযোগ্যভাবে দ্রুত হয়, বা যদি খাঁটি গণিত উভয় ভাষায় উল্লেখযোগ্যভাবে দ্রুত হয় তবে স্ট্রিং ক্রিয়াকলাপ, মেমরি পরিচালনা এবং আরও কয়েকটি জিনিস সহজেই একটি ভাষাকে আলোকিত করতে পারে।
ম্যাক্স ল্যাবার্ট

1
সি ++ আইওস্ট্রিমের চেয়ে আরও ভাল (দ্রুত) করা সহজ। তবে এটি মূলত একটি গ্রন্থাগার বাস্তবায়ন সমস্যা (সর্বাধিক জনপ্রিয় বিক্রেতাদের পরিচিত সংস্করণগুলিতে)।
বেন ভয়েগ্ট

4

আপনি সি কোড ডি লিখতে পারেন যতদূর দ্রুত এটি অনেক কিছুর উপর নির্ভর করবে:

  • কি সংকলক আপনি ব্যবহার
  • আপনি কি বৈশিষ্ট্য ব্যবহার
  • আপনি কতটা আক্রমণাত্মকভাবে অনুকূলিত হন

প্রথমটির মধ্যে পার্থক্যগুলি টেনে আনতে মোটামুটি উপযুক্ত নয় second দ্বিতীয়টি সি ++ কে একটি সুবিধা দিতে পারে কারণ যদি কিছু হয় তবে কম ভারী বৈশিষ্ট্য রয়েছে। তৃতীয়টি মজাদার একটি: ডি কোড কিছু উপায়ে অপ্টিমাইজ করা সহজ কারণ সাধারণভাবে এটি বোঝা সহজ। এছাড়াও এতে ভারবোস এবং পুনরাবৃত্তিমূলক তবে দ্রুত কোডের মতো জিনিসগুলি সংক্ষিপ্ত আকারে লেখার মঞ্জুরি দিয়ে বৃহত পরিমাণে জেনারেটরি প্রোগ্রামিং করার ক্ষমতা রয়েছে।


3

বাস্তবায়ন ইস্যুটির একটি মানের মতো মনে হচ্ছে। উদাহরণস্বরূপ, আমি এখানে যা পরীক্ষা করেছি তা এখানে:

import std.datetime, std.stdio, std.random;

version = ManualInline;

immutable N = 20000;
immutable Size = 10;

alias int value_type;
alias long result_type;
alias value_type[] vector_type;

result_type scalar_product(in vector_type x, in vector_type y)
in
{
    assert(x.length == y.length);
}
body
{
    result_type result = 0;

    foreach(i; 0 .. x.length)
        result += x[i] * y[i];

    return result;
}

void main()
{   
    auto startTime = Clock.currTime();

    // 1. allocate vectors
    vector_type[] vectors = new vector_type[N];
    foreach(ref vec; vectors)
        vec = new value_type[Size];

    auto time = Clock.currTime() - startTime;
    writefln("allocation: %s ", time);
    startTime = Clock.currTime();

    // 2. randomize vectors
    foreach(ref vec; vectors)
        foreach(ref e; vec)
            e = uniform(-1000, 1000);

    time = Clock.currTime() - startTime;
    writefln("random: %s ", time);
    startTime = Clock.currTime();

    // 3. compute all pairwise scalar products
    result_type avg = 0;

    foreach(vecA; vectors)
        foreach(vecB; vectors)
        {
            version(ManualInline)
            {
                result_type result = 0;

                foreach(i; 0 .. vecA.length)
                    result += vecA[i] * vecB[i];

                avg += result;
            }
            else
            {
                avg += scalar_product(vecA, vecB);
            }
        }

    avg = avg / (N * N);

    time = Clock.currTime() - startTime;
    writefln("scalar products: %s ", time);
    writefln("result: %s", avg);
}

সঙ্গে ManualInlineসংজ্ঞায়িত আমি 28 সেকেন্ড পেতে, কিন্তু আমি ছাড়া 32 পেতে তাই কম্পাইলার এমনকি এই সহজ ফাংশন, যা আমার মনে হয় এটা এটা করা উচিত পরিষ্কার ইনলাইনিং করা হয় না।

(আমার কমান্ড লাইনটি dmd -O -noboundscheck -inline -release ...।)


1
আপনি আপনার সি ++ সময়ের সাথে তুলনা না করলে আপনার সময় অর্থহীন।
ছদ্মবেশী কাবিয়ার

3
@ ড্যানিয়েল: আপনি পয়েন্টটি মিস করছেন। এটি বিচ্ছিন্নভাবে ডি অপ্টিমাইজেশানগুলি প্রদর্শন করা ছিল, যেমনটি আমি বলেছিলাম: "সুতরাং সংকলক এমনকি এই সাধারণ ফাংশনটিও অন্তর্ভুক্ত করছে না, যা আমি মনে করি এটি পরিষ্কার হওয়া উচিত।" এমনকি আমি এটি সি ++ এর সাথে তুলনা করার চেষ্টা করছি, যেমনটি আমি প্রথম বাক্যে স্পষ্টভাবে বলেছি : "বাস্তবায়ন ইস্যুটির মানের মতো বলে মনে হয়।"
GManNickG

আহ সত্য, দুঃখিত :) আপনি এটিও দেখতে পাবেন যে ডিএমডি সংকলকটি উভয়ই লুপগুলিকে ভেক্টরাইজ করে না।
deceleratedcaviar
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.