D nhanh như thế nào so với C ++?


133

Tôi thích một số tính năng của D, nhưng sẽ thích thú nếu chúng đi kèm với hình phạt thời gian chạy?

Để so sánh, tôi đã thực hiện một chương trình đơn giản tính toán các sản phẩm vô hướng của nhiều vectơ ngắn cả trong C ++ và D. Kết quả thật đáng ngạc nhiên:

  • D: 18,9 s [xem bên dưới để biết thời gian chạy cuối cùng]
  • C ++: 3,8 giây

C ++ thực sự nhanh gấp gần năm lần hay tôi đã mắc lỗi trong chương trình D?

Tôi đã biên dịch C ++ với g ++ -O3 (gcc-snapshot 2011/02/19) và D với dmd -O (dmd 2.052) trên máy tính để bàn linux vừa phải. Các kết quả có thể lặp lại qua nhiều lần chạy và độ lệch chuẩn không đáng kể.

Đây là chương trình 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;
}

Và đây là phiên bản 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;
}

3
Nhân tiện, chương trình của bạn có một lỗi trên dòng này: avg = avg / N*N(thứ tự các hoạt động).
Vladimir Panteleev

4
Bạn có thể thử viết lại mã bằng cách sử dụng các hoạt động mảng / vector digitalmars.com/d/2.0/arrays.html
Michal Minich

10
Để cung cấp một so sánh tốt hơn, bạn nên sử dụng cùng một trình biên dịch back-end. DMD và DMC ++ hoặc GDC và G ++
he_the_great

1
@Sion Sheevok Thật không may, hồ sơ dmd dường như không có sẵn cho Linux? (vui lòng sửa lại cho tôi nếu tôi sai, nhưng nếu tôi nói dmd ... trace.deftôi nhận được error: unrecognized file extension def. Và tài liệu dmd cho optlink chỉ đề cập đến Windows.
Lars

1
Ah, không bao giờ quan tâm đến tập tin .def mà nó phun ra. Thời gian nằm trong tệp .log. "Nó chứa danh sách các hàm theo thứ tự trình liên kết sẽ liên kết chúng" - có lẽ điều đó giúp optlink tối ưu hóa một cái gì đó? Cũng lưu ý rằng "Ngoài ra, ld hỗ trợ đầy đủ các tệp" * .def "tiêu chuẩn, có thể được chỉ định trên dòng lệnh của trình liên kết như tệp đối tượng" - vì vậy bạn có thể thử truyền dấu vết.def qua -L nếu bạn muốn đến.
Trass3r

Câu trả lời:


64

Để bật tất cả tối ưu hóa và vô hiệu hóa tất cả các kiểm tra an toàn, hãy biên dịch chương trình D của bạn với các cờ DMD sau:

-O -inline -release -noboundscheck

EDIT : Tôi đã thử các chương trình của bạn với g ++, dmd và gdc. dmd không bị tụt lại phía sau, nhưng gdc đạt được hiệu năng rất gần với g ++. Dòng lệnh tôi đã sử dụng là gdmd -O -release -inline(gdmd là một trình bao bọc xung quanh gdc chấp nhận các tùy chọn dmd).

Nhìn vào danh sách trình biên dịch chương trình, có vẻ như cả dmd và gdc đều không được nội tuyến scalar_product, nhưng g ++ / gdc đã phát ra các hướng dẫn MMX, vì vậy chúng có thể tự động vector hóa vòng lặp.


3
@CyberShadow: nhưng nếu bạn xóa kiểm tra an toàn ... bạn sẽ không mất một số tính năng quan trọng của D?
Matthieu M.

33
Bạn đang mất các tính năng mà C ++ chưa bao giờ có. Hầu hết các ngôn ngữ không cung cấp cho bạn một sự lựa chọn.
Vladimir Panteleev

6
@CyberShadow: chúng ta có thể nghĩ đây là một dạng gỡ lỗi và phát hành bản dựng không?
Francesco

7
@Bernard: in -release, kiểm tra giới hạn bị tắt cho tất cả các mã ngoại trừ các chức năng an toàn. để thực sự tắt giới hạn kiểm tra, hãy sử dụng cả kiểm tra và giải phóng.
Michal Minich

5
@CyberShadow Cảm ơn! Với những cờ thời gian chạy cải thiện đáng kể. Bây giờ D đang ở mức 12,9 giây. Nhưng vẫn chạy lâu hơn 3 lần. @Matthieu M. Tôi sẽ không ngại kiểm tra một chương trình với giới hạn kiểm tra trong chuyển động chậm và một khi nó được gỡ lỗi, hãy để nó thực hiện các tính toán của nó mà không cần kiểm tra giới hạn. (Bây giờ tôi cũng làm như vậy với C ++.)
Lars

32

Một điều lớn làm chậm D là triển khai thu gom rác phụ. Các điểm chuẩn không quá căng thẳng, GC sẽ cho thấy hiệu năng rất giống với mã C và C ++ được biên dịch với cùng một phụ trợ trình biên dịch. Điểm chuẩn làm căng thẳng nặng nề của GC sẽ cho thấy D thực hiện rất nhiều. Tuy nhiên, hãy yên tâm, đây là một vấn đề chất lượng thực hiện (mặc dù nghiêm trọng), không phải là sự bảo đảm của sự chậm chạp. Ngoài ra, D cung cấp cho bạn khả năng từ chối sử dụng GC và điều chỉnh quản lý bộ nhớ trong các bit quan trọng về hiệu năng, trong khi vẫn sử dụng nó trong 95% mã ít quan trọng về hiệu năng của mã.

Gần đây tôi đã nỗ lực cải thiện hiệu suất của GC và kết quả khá ấn tượng, ít nhất là trên các điểm chuẩn tổng hợp. Hy vọng những thay đổi này sẽ được tích hợp vào một trong vài bản phát hành tiếp theo và sẽ giảm thiểu vấn đề.


1
Tôi nhận thấy một trong những thay đổi của bạn là thay đổi từ phân chia sang thay đổi bit. Không phải đó là một cái gì đó trình biên dịch làm?
GManNickG

3
@GMan: Có, nếu giá trị bạn chia cho được biết tại thời điểm biên dịch. Không, nếu giá trị chỉ được biết trong thời gian chạy, đó là trường hợp tôi thực hiện tối ưu hóa đó.
dsimcha

@dsimcha: Hừm. Tôi hình dung nếu bạn biết để làm cho nó, trình biên dịch cũng có thể. Chất lượng của vấn đề triển khai, hoặc tôi thiếu rằng một số điều kiện cần phải được thỏa mãn mà trình biên dịch không thể chứng minh, nhưng bạn biết không? (Hiện tại tôi đang học D, vì vậy những điều nhỏ nhặt về trình biên dịch này đột nhiên rất thú vị đối với tôi. :))
GManNickG

13
@GMan: Dịch chuyển bit chỉ hoạt động nếu số bạn chia cho là lũy thừa của hai. Trình biên dịch không thể chứng minh điều này nếu số chỉ được biết trong thời gian chạy, và kiểm tra và phân nhánh sẽ chậm hơn so với chỉ sử dụng lệnh div. Trường hợp của tôi là bất thường bởi vì giá trị chỉ được biết đến trong thời gian chạy, nhưng tôi biết tại thời điểm biên dịch rằng nó sẽ có sức mạnh bằng hai.
dsimcha

7
Lưu ý rằng chương trình được đăng trong ví dụ này không phân bổ trong phần tốn thời gian.
Vladimir Panteleev

27

Đây là một chủ đề rất hướng dẫn, cảm ơn tất cả các công việc cho OP và người trợ giúp.

Một lưu ý - bài kiểm tra này không đánh giá câu hỏi chung về tính trừu tượng / hình phạt tính năng hoặc thậm chí là chất lượng phụ trợ. Nó tập trung vào hầu như một tối ưu hóa (tối ưu hóa vòng lặp). Tôi nghĩ thật công bằng khi nói rằng phần phụ trợ của gcc có phần tinh tế hơn so với dmd, nhưng sẽ là một sai lầm khi cho rằng khoảng cách giữa chúng là lớn đối với tất cả các nhiệm vụ.


4
Tôi hoàn toàn đồng ý. Như được thêm vào sau này, tôi chủ yếu quan tâm đến hiệu suất cho các tính toán số trong đó tối ưu hóa vòng lặp có lẽ là điều quan trọng nhất. Những tối ưu hóa nào khác mà bạn nghĩ sẽ quan trọng đối với điện toán số? Và những tính toán nào sẽ kiểm tra chúng? Tôi sẽ quan tâm để bổ sung cho bài kiểm tra của mình và thực hiện thêm một số bài kiểm tra (nếu chúng đơn giản như vậy). Nhưng evtl. Đây là một câu hỏi khác trong chính nó?
Lars

11
Là một kỹ sư cắt răng trên C ++, bạn là một anh hùng của tôi. Trân trọng, tuy nhiên, đây nên là một bình luận, không phải là một câu trả lời.
Alan

14

Chắc chắn có vẻ như một vấn đề chất lượng thực hiện.

Tôi đã chạy một số thử nghiệm với mã của OP và thực hiện một số thay đổi. Tôi thực sự có D đi nhanh hơn cho LDC / clang ++, hoạt động dựa trên giả định rằng các mảng phải được phân bổ động ( xsvà vô hướng liên kết). Xem dưới đây cho một số số.

Câu hỏi cho OP

Có phải là cố ý rằng cùng một hạt giống được sử dụng cho mỗi lần lặp của C ++, trong khi không phải như vậy đối với D?

Thiết lập

Tôi đã điều chỉnh nguồn D gốc (được đặt tên scalar.d) để làm cho nó di động giữa các nền tảng. Điều này chỉ liên quan đến việc thay đổi loại số được sử dụng để truy cập và sửa đổi kích thước của mảng.

Sau này, tôi đã thực hiện các thay đổi sau:

  • Được sử dụng uninitializedArrayđể tránh các thông số mặc định cho vô hướng trong xs (có thể tạo ra sự khác biệt lớn nhất). Điều này rất quan trọng vì D thường mặc định nhập mọi thứ một cách âm thầm, điều mà C ++ không có.

  • Đã in mã ra và thay thế writeflnbằngwriteln

  • Thay đổi nhập khẩu để được chọn lọc
  • Toán tử pow đã sử dụng ( ^^) thay vì nhân thủ công cho bước tính toán trung bình cuối cùng
  • Đã xóa size_typevà thay thế một cách thích hợp bằng index_typebí danh mới

... do đó dẫn đến scalar2.cpp( pastebin ):

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

Sau khi thử nghiệm scalar2.d(ưu tiên tối ưu hóa cho tốc độ), vì tò mò tôi đã thay thế các vòng lặp mainbằng các foreachtương đương và gọi nó scalar3.d( pastebin ):

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

Tôi đã biên dịch từng thử nghiệm này bằng trình biên dịch dựa trên LLVM, vì LDC dường như là tùy chọn tốt nhất để biên dịch D về mặt hiệu năng. Trên bản cài đặt x86_64 Arch Linux của tôi, tôi đã sử dụng các gói sau:

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

Tôi đã sử dụng các lệnh sau để biên dịch từng lệnh:

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

Các kết quả

Kết quả ( ảnh chụp màn hình của đầu ra giao diện điều khiển thô ) của từng phiên bản của nguồn như sau:

  1. scalar.cpp (bản gốc C ++):

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

    C ++ đặt tiêu chuẩn ở 2582 ms .

  2. scalar.d (nguồn OP đã sửa đổi):

    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 

    Điều này chạy trong ~ 2957 ms . Chậm hơn so với triển khai C ++, nhưng không quá nhiều.

  3. scalar2.d (thay đổi loại chỉ mục / độ dài và tối ưu hóa uninitializedArray):

    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

    Nói cách khác, ~ 1860 ms . Cho đến nay điều này là trong đầu.

  4. scalar3.d (lời khuyên):

    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

    ~ 2182 ms chậm hơn scalar2.d, nhưng nhanh hơn phiên bản C ++.

Phần kết luận

Với các tối ưu hóa chính xác, việc triển khai D thực sự diễn ra nhanh hơn so với triển khai C ++ tương đương của nó bằng các trình biên dịch dựa trên LLVM có sẵn. Khoảng cách hiện tại giữa D và C ++ đối với hầu hết các ứng dụng dường như chỉ dựa trên những hạn chế của việc triển khai hiện tại.


8

dmd là triển khai tham chiếu của ngôn ngữ và do đó, hầu hết các công việc được đưa vào frontend để sửa lỗi thay vì tối ưu hóa phụ trợ.

"Trong" nhanh hơn trong trường hợp của bạn vì bạn đang sử dụng mảng động là loại tham chiếu. Với ref, bạn giới thiệu một mức độ gián tiếp khác (thường được sử dụng để thay đổi chính mảng và không chỉ nội dung).

Các vectơ thường được thực hiện với các cấu trúc trong đó const ref có ý nghĩa hoàn hảo. Xem smallptD so với smallpt để biết ví dụ trong thế giới thực có vô số thao tác vectơ và tính ngẫu nhiên.

Lưu ý rằng 64-Bit cũng có thể tạo ra sự khác biệt. Tôi đã từng bỏ lỡ rằng trên x64 gcc biên dịch mã 64-bit trong khi dmd vẫn mặc định là 32 (sẽ thay đổi khi tiền mã hóa 64-bit đáo hạn). Có một sự tăng tốc đáng chú ý với "dmd -m64 ...".


7

Việc C ++ hay D nhanh hơn có khả năng phụ thuộc nhiều vào những gì bạn đang làm. Tôi nghĩ rằng khi so sánh C ++ được viết tốt với mã D được viết tốt, nhìn chung chúng sẽ có tốc độ tương tự hoặc C ++ sẽ nhanh hơn, nhưng những gì trình biên dịch cụ thể quản lý để tối ưu hóa có thể có tác động lớn hoàn toàn ngoài ngôn ngữ chinh no.

Tuy nhiên, có một vài trường hợp D đứng một cơ hội tốt để đánh bại C ++ cho tốc độ. Cái chính mà tôi nghĩ đến sẽ là xử lý chuỗi. Nhờ các khả năng cắt lát mảng của D, các chuỗi (và các mảng nói chung) có thể được xử lý nhanh hơn nhiều so với bạn có thể làm trong C ++. Đối với D1, bộ xử lý XML của Tango cực kỳ nhanh , chủ yếu nhờ vào khả năng cắt mảng của D (và hy vọng rằng D2 sẽ có trình phân tích cú pháp XML nhanh tương tự sau khi bộ xử lý hiện đang hoạt động cho Phobos đã hoàn thành). Vì vậy, cuối cùng, liệu D hay C ++ sẽ nhanh hơn hay không sẽ phụ thuộc rất nhiều vào những gì bạn đang làm.

Bây giờ, tôi đang ngạc nhiên mà bạn đang nhìn thấy một sự khác biệt như vậy trong tốc độ trong trường hợp đặc biệt này, nhưng nó là loại điều mà tôi mong chờ để cải thiện như DMD được cải thiện. Sử dụng gdc có thể mang lại kết quả tốt hơn và có thể sẽ là một so sánh gần hơn với chính ngôn ngữ (chứ không phải là phụ trợ) cho rằng nó dựa trên gcc. Nhưng nó sẽ không làm tôi ngạc nhiên chút nào nếu có một số điều có thể được thực hiện để tăng tốc mã mà dmd tạo ra. Tôi không nghĩ rằng có nhiều câu hỏi rằng gcc trưởng thành hơn dmd vào thời điểm này. Và tối ưu hóa mã là một trong những thành quả chính của sự trưởng thành mã.

Cuối cùng, điều quan trọng là dmd hoạt động tốt như thế nào cho ứng dụng cụ thể của bạn, nhưng tôi đồng ý rằng chắc chắn sẽ rất tốt nếu biết C ++ và D so sánh tốt như thế nào. Về lý thuyết, chúng nên giống nhau khá nhiều, nhưng nó thực sự phụ thuộc vào việc thực hiện. Tôi nghĩ rằng một bộ điểm chuẩn toàn diện sẽ được yêu cầu để thực sự kiểm tra xem hai hiện tại so sánh tốt như thế nào.


4
Có, tôi sẽ ngạc nhiên nếu đầu vào / đầu ra nhanh hơn đáng kể trong cả hai ngôn ngữ hoặc nếu toán học thuần túy nhanh hơn đáng kể trong cả hai ngôn ngữ, nhưng hoạt động chuỗi, quản lý bộ nhớ và một vài thứ khác có thể dễ dàng cho phép một ngôn ngữ tỏa sáng.
Max Lybbert

1
Thật dễ dàng để làm tốt hơn (nhanh hơn) so với iostreams C ++. Nhưng đó chủ yếu là vấn đề triển khai thư viện (trên tất cả các phiên bản đã biết từ các nhà cung cấp phổ biến nhất).
Ben Voigt

4

Bạn có thể viết mã C là D sao cho nhanh hơn, nó sẽ phụ thuộc vào rất nhiều thứ:

  • Bạn sử dụng trình biên dịch nào
  • Bạn sử dụng tính năng gì
  • bạn tích cực tối ưu hóa như thế nào

Sự khác biệt trong lần đầu tiên không công bằng khi kéo vào. Lần thứ hai có thể mang lại lợi thế cho C ++ vì nếu có bất cứ điều gì, có ít tính năng nặng hơn. Thứ ba là một điều thú vị: Mã D theo một số cách dễ dàng tối ưu hóa hơn vì nói chung nó dễ hiểu hơn. Ngoài ra, nó có khả năng thực hiện một mức độ lớn về lập trình tổng quát cho phép những thứ như dài dòng và mã lặp đi lặp lại nhưng nhanh chóng được viết dưới dạng ngắn hơn.


3

Có vẻ như một chất lượng của vấn đề thực hiện. Ví dụ: đây là những gì tôi đã thử nghiệm với:

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

Với ManualInlineđịnh nghĩa tôi nhận được 28 giây, nhưng không có tôi nhận được 32. Vì vậy, trình biên dịch thậm chí không nội tuyến chức năng đơn giản này, mà tôi nghĩ rằng nó rõ ràng nên được.

(Dòng lệnh của tôi là dmd -O -noboundscheck -inline -release ....)


1
Thời gian của bạn là vô nghĩa trừ khi bạn đưa ra so sánh với thời gian C ++ của bạn.
giảm tốc

3
@Daniel: Bạn đang thiếu điểm. Đó là để chứng minh sự tối ưu hóa D trong sự cô lập, cụ thể là cho kết luận mà tôi đã tuyên bố: "Vì vậy, trình biên dịch thậm chí không nội tuyến chức năng đơn giản này, mà tôi nghĩ rằng nó nên rõ ràng." Tôi thậm chí đang cố gắng so sánh nó với C ++, như tôi đã nói rõ ràng trong câu đầu tiên : "Có vẻ như là một vấn đề chất lượng thực hiện."
GManNickG

À đúng rồi, xin lỗi :). Bạn cũng sẽ thấy trình biên dịch DMD không vectơ các vòng lặp cả.
giảm tốc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.