2010-03-23 96 views
5

Tôi đã lo lắng về tốc độ của C# khi nó đề cập đến tính toán nặng, khi bạn cần sử dụng nguồn CPU nguyên.C++ và C# tốc độ so sánh

Tôi luôn nghĩ rằng C++ nhanh hơn nhiều so với C# khi nói đến tính toán. Vì vậy, tôi đã làm một số xét nghiệm nhanh. Thử nghiệm đầu tiên tính số nguyên tố < một số nguyên n, phép thử thứ hai tính một số số pandigital. Ý tưởng cho thử nghiệm thứ hai xuất phát từ đây: Pandigital Numbers

C# Thủ tính toán:

using System; 
using System.Diagnostics; 

class Program 
{ 

    static int primes(int n) 
    { 

     uint i, j; 
     int countprimes = 0; 

     for (i = 1; i <= n; i++) 
     { 
      bool isprime = true; 

      for (j = 2; j <= Math.Sqrt(i); j++) 

       if ((i % j) == 0) 
       { 
        isprime = false; 
        break; 
       } 

      if (isprime) countprimes++; 
     } 

     return countprimes; 
    } 



    static void Main(string[] args) 
    { 
     int n = int.Parse(Console.ReadLine()); 
     Stopwatch sw = new Stopwatch(); 

     sw.Start(); 
     int res = primes(n); 
     sw.Stop(); 
     Console.WriteLine("I found {0} prime numbers between 0 and {1} in {2} msecs.", res, n, sw.ElapsedMilliseconds); 
     Console.ReadKey(); 
    } 
} 

C++ biến thể:

#include <iostream> 
#include <ctime> 
#include <cmath> 

int primes(unsigned long n) { 
unsigned long i, j; 
int countprimes = 0; 
    for(i = 1; i <= n; i++) { 
     int isprime = 1; 
     for(j = 2; j < sqrt((float)i); j++) 
      if(!(i%j)) { 
     isprime = 0; 
     break; 
    } 
    countprimes+= isprime; 
    } 
    return countprimes; 
} 

int main() { 
int n, res; 
cin>>n; 
unsigned int start = clock(); 

res = primes(n); 
int tprime = clock() - start; 
cout<<"\nI found "<<res<<" prime numbers between 1 and "<<n<<" in "<<tprime<<" msecs."; 
return 0; 
} 

Khi tôi chạy thử cố gắng tìm số nguyên tố < hơn 100.000, C# biến thể hoàn thành trong 0,409 giây và biến thể C++ trong 0,614 giây. Khi tôi chạy chúng với 1.000.000 C# đã hoàn thành trong 6.039 giây và C++ trong khoảng 12.987 giây.

kiểm tra Pandigital trong C#:

using System; 
using System.Diagnostics; 

class Program 
{ 
    static bool IsPandigital(int n) 
    { 
     int digits = 0; int count = 0; int tmp; 

     for (; n > 0; n /= 10, ++count) 
     { 
      if ((tmp = digits) == (digits |= 1 << (n - ((n/10) * 10) - 1))) 
       return false; 
     } 

     return digits == (1 << count) - 1; 
    } 

    static void Main() 
    { 
     int pans = 0; 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 

     for (int i = 1; i <= 123456789; i++) 
     { 
      if (IsPandigital(i)) 
      { 
       pans++; 
      } 
     } 
     sw.Stop(); 
     Console.WriteLine("{0}pcs, {1}ms", pans, sw.ElapsedMilliseconds); 
     Console.ReadKey(); 
    } 
} 

kiểm tra Pandigital trong C++:

#include <iostream> 
#include <ctime> 

using namespace std; 

int IsPandigital(int n) 
    { 
     int digits = 0; int count = 0; int tmp; 

     for (; n > 0; n /= 10, ++count) 
     { 
      if ((tmp = digits) == (digits |= 1 << (n - ((n/10) * 10) - 1))) 
       return 0; 
     } 

     return digits == (1 << count) - 1; 
    } 


int main() { 
    int pans = 0; 
    unsigned int start = clock(); 

    for (int i = 1; i <= 123456789; i++) 
    { 
     if (IsPandigital(i)) 
     { 
     pans++; 
     } 
    } 
    int ptime = clock() - start; 
    cout<<"\nPans:"<<pans<<" time:"<<ptime; 
    return 0; 
} 

C# biến thể chạy trong 29,906 giây và C++ trong khoảng 36,298 giây.

Tôi không chạm vào bất kỳ công tắc trình biên dịch nào và cả hai chương trình C# và C++ đều được biên dịch với các tùy chọn gỡ lỗi. Trước khi tôi cố gắng chạy thử nghiệm, tôi đã lo lắng rằng C# sẽ tụt hậu tốt so với C++, nhưng bây giờ có vẻ như có một sự khác biệt khá lớn về tốc độ trong C# favor.

Ai đó có thể giải thích điều này? C# được trích xuất và C++ được biên dịch tự nhiên vì vậy bình thường C++ sẽ nhanh hơn một biến thể C#.

Cảm ơn câu trả lời!

Tôi đã xóa tất cả các kiểm tra cho cấu hình Phát hành.

Thử nghiệm đầu tiên (số nguyên tố)

C# (số < 100,0000): 0,189 giây C++ (số < 100,0000): 0,036 giây

C# (nummbers < 1.000.000): 5.300 giây C++ (nummbers < 1.000.000): 1,166 giây

thử nghiệm thứ hai (số Pandigital):

C#: 21. 224 giây C++: 4.104 giây

Vì vậy, mọi thứ đã thay đổi, giờ đây C++ nhanh hơn rất nhiều. Lỗi của tôi là tôi đã chạy thử nghiệm cho cấu hình Debug. Tôi có thể thấy một số cải thiện tốc độ nếu tôi chạy các tập tin thực thi C# thông qua ngen?

Lý do tôi cố gắng so sánh C# và C++ là vì tôi biết một số khái niệm cơ bản của cả hai và tôi muốn tìm hiểu một giao dịch API với GUI.Tôi nghĩ WPF rất tốt vì tôi đang nhắm đến desktop, tôi muốn xem liệu C# có thể cung cấp đủ tốc độ và hiệu suất khi sử dụng sức mạnh CPU tuyệt đối để tính toán các tính toán khác nhau (lưu trữ tệp, mật mã, codec, v.v ...) . Nhưng có vẻ như C# đáng buồn không thể theo kịp với C++ khi nói đến tốc độ.

Vì vậy, tôi giả định rằng tôi sẽ mãi mãi bị mắc kẹt với câu hỏi này Tough question on WPF, Win32, MFC và tôi sẽ tìm một API phù hợp mới hơn.

+5

"Ai có thể giải thích điều này? C# được trích xuất và C++ được biên dịch tự nhiên nên bình thường C++ sẽ nhanh hơn biến thể C#". <- Không theo chế độ gỡ lỗi. –

+6

"... Chương trình C++ được biên dịch với các tùy chọn gỡ lỗi". Vậy tại sao chúng ta lo lắng về hiệu suất? – GManNickG

+4

'= 2; j <(i^(1/2)); j ++) 'Mã này sai. Bạn đang làm một chút - hoặc, không phải là một số mũ. –

Trả lời

8

Tại sao bạn cho rằng mã jitted chậm hơn mã gốc? Hình phạt tốc độ duy nhất sẽ là sự đánh nhau thực sự, chỉ xảy ra một lần (nói chung). Với một chương trình có thời gian chạy 30 giây, chúng ta đang nói về một phần nhỏ của tổng chi phí.

Tôi nghĩ rằng bạn có thể nhầm lẫn mã được jitted với diễn giải mã, được biên dịch từng dòng. Có một sự khác biệt khá đáng kể giữa hai người.

Khi những người khác đã chỉ ra, bạn cũng cần chạy trong chế độ phát hành; chế độ gỡ lỗi tắt tắt tối đa tối ưu hóa nhất, do đó cả hai phiên bản sẽ chậm hơn so với mức độ cần thiết (nhưng bằng số tiền khác nhau).

Sửa - Tôi phải chỉ ra một điều khác, đó là dòng này:

for (j = 2; j <= Math.Sqrt(i); j++) 

là cực kỳ hiệu quả và có thể can thiệp vào điểm chuẩn. Bạn nên tính toán Math.Sqrt(i)bên ngoài vòng lặp bên trong. Có thể điều này sẽ làm chậm cả hai phiên bản bằng một số tiền tương đương, nhưng tôi không chắc chắn, các trình biên dịch khác nhau sẽ thực hiện các tối ưu hóa khác nhau.

+1

+1. C++ bị nhiều hơn từ các tối ưu hóa bị tắt vì nó dựa trên nội tuyến để thực hiện. Ngoài ra, JIT sẽ thực hiện một số tối ưu hóa trên C# ngay cả khi nhị phân được xây dựng trong chế độ gỡ lỗi. –

+2

Không chỉ tắt hầu hết các tối ưu hóa, nó có thể thêm một loạt các kiểm tra lỗi không liên quan. Visual Studio, ví dụ, thêm kiểm tra tràn, kiểm tra đống và nhiều thứ thú vị khác trong cấu hình chế độ gỡ lỗi mặc định. – kibibu

4

Biên dịch lại chương trình C++ với bật tối ưu hóa đầy đủ và chạy lại các thử nghiệm. C# jit sẽ tối ưu hóa mã khi nó được trích xuất để bạn so sánh mã C# /. NET được tối ưu hóa với C++ chưa được tối ưu hóa.

11

Bạn cần phải biên dịch C++ ở chế độ phát hành và cho phép tối ưu hóa để có được kết quả hiệu suất mà bạn đang tìm kiếm.

+0

Tôi cũng sẽ thử điều đó. – Mack

12

máy phát điện hàng đầu trong C++ là không đúng

i^(1/2) == i xor 0

^là các nhà điều hành Bitwise xor và/là số nguyên chia.

1st chỉnh sửa, đó là đúng nhưng ineffecient: Kể từ khi tôi xor 0 == i, sàng không chỉ dừng lại ở sqrt (i) nhưng tại i.

2nd chỉnh sửa:

Các sàng có thể được thực hiện một chút hiệu quả hơn. (Bạn chỉ cần tính toán sqrt (n)). Đây là cách tôi thực hiện Sieve of Eratosthenes để sử dụng riêng của tôi (đây là trong C99 dù):

void sieve(const int n, unsigned char* primes) 
{ 
     memset(primes, 1, (n+1) * sizeof(unsigned char)); 

     // sieve of eratosthenes 
     primes[0] = primes[1] = 0; 
     int m = floor(sqrt(n)); 
     for (int i = 2; i <= m; i++) 
       if (primes[i]) // no need to remove multiples of i if it is not prime 
         for (int j = i; j <= (n/i); j++) 
           primes[i*j] = 0; 
} 
+0

Tôi đã sửa nó. Cảm ơn. – Mack

+0

Tôi tự hỏi, dù điều này có thực sự làm cho nó chạy chậm hơn hay không, bởi vì phiên bản C# thực sự đánh giá 'Math.Sqrt (i)' trên mỗi lần lặp lại, đắt hơn rất nhiều lần so với XOR đơn hướng, do đó, lỗi nhỏ này thực sự có thể làm cho phiên bản C++ * nhanh hơn * bởi vì cả hai phiên bản của chương trình đều không hiệu quả. – Aaronaught

+2

@Aaronaught: Ngoại trừ phiên bản C# là vòng lặp tới Sqrt (i) và phiên bản C++ đang lặp với i. –

2

Thứ nhất, không bao giờ làm tiêu chuẩn như vậy trong chế độ gỡ lỗi. Để có được số lượng có ý nghĩa luôn sử dụng chế độ phát hành.

JIT có lợi thế là biết nền tảng mà nó chạy, trong khi mã được biên dịch trước có thể tối ưu hóa cho nền tảng đang chạy.

+1

Có thể, nhưng có tất nhiên chi phí thực sự làm việc biên dịch trên nền tảng đó. –

6

Mất nhiều thời gian hơn vì thuật toán sai.

for(j = 2; j < (i^(1/2)); j++) 

cũng giống như

for(j = 2; j < (i^0); j++) 

cũng giống như

for(j = 2; j < i; j++) 

i là lớn hơn rất nhiều so với sqrt (i). Nhìn vào thời gian chạy, nó là một thứ tự độ lớn lớn hơn mà nó nên có trong việc thực hiện C++.

Ngoài ra, giống như mọi người khác đang nói, tôi không nghĩ việc kiểm tra hiệu suất ở chế độ gỡ lỗi là hợp lý.

+0

Tôi đã sửa lỗi, tôi xin lỗi. Tôi cũng sẽ cố gắng chạy chúng trong chế độ phát hành. – Mack

0

Cả hai kiểm tra đều không hợp lệ vì bạn đã biên soạn mà không cần tối ưu hóa.

Thử nghiệm đầu tiên là vô nghĩa ngay cả khi so sánh hành vi không được tối ưu hóa, do lỗi trong mã của bạn; Math.Sqrt(i) trả về căn bậc hai của i, i^(1/2) trả về i - vì vậy C++ đang làm nhiều công việc hơn C#.

Nói chung, đây không phải là điều hữu ích để làm - bạn đang cố gắng tạo một điểm chuẩn tổng hợp có ít hoặc không liên quan đến việc sử dụng trong thế giới thực.

2

Đó là một huyền thoại dai dẳng rằng trình biên dịch JIT trong mã được quản lý tạo mã máy kém hiệu quả hơn nhiều so với trình biên dịch C/C++. Mã được quản lý thường thắng về quản lý bộ nhớ và toán toán dấu chấm động, C/C++ thường thắng khi trình tối ưu hóa mã có thể dành nhiều thời gian hơn để tối ưu hóa mã. Nói chung, mã được quản lý là khoảng 80% nhưng nó hoàn toàn phụ thuộc vào 10% mã mà chương trình dành 90% thời gian của nó.

Thử nghiệm của bạn sẽ không hiển thị điều này, bạn đã không bật trình tối ưu hóa và không có nhiều thứ để tối ưu hóa.

+0

"Mã được quản lý thường thắng trên quản lý bộ nhớ" <- Bởi vì các lập trình viên C/C++ thường lười biếng và chỉ sử dụng bộ đệm khổng lồ thay vì một cái gì đó giống như 'std :: vector ', vốn phổ biến trong các ngôn ngữ JIT. Tốc độ của cả mã được biên dịch và mã JIT phải giống nhau, tất cả đã nói và được thực hiện, miễn là bạn giảm thời gian để tải jitter vào bộ nhớ (có thể khá lớn trong trường hợp của Java: P) +1 –

+1

Mã được quản lý khó có thể giành được trên các hoạt động điểm nổi vì hiện tại .Net JIT không phát ra bất kỳ lệnh SSE nào. –

+0

Có nhiều hơn một thẻ x64. Ví dụ: http://stackoverflow.com/questions/686483/c-vs-c-big-performance-difference/687741#687741 –

1

guys, trước khi so sánh tốc độ chương trình với nhau, vui lòng đọc một số bài viết về hướng dẫn cpu, lắp ráp, quản lý bộ nhớ cache v.v. Và tác giả chỉ là một người bạn vui nhộn. Kiểm tra hiệu suất của một bản dựng gỡ lỗi.

Billy O'Neal - sự khác nhau giữa việc phân bổ bộ đệm lớn và chỉ sử dụng một phần nhỏ của nó và sử dụng các thứ được phân bổ động như vectơ trong từ ngữ thấp là gì? Khi bộ đệm lớn được cấp phát - không ai bận tâm về những thứ không được sử dụng. Không cần thêm các hoạt động hỗ trợ nữa. Trong khi cho những thứ năng động như vector - kiểm tra liên tục của giới hạn bộ nhớ yêu cầu không vượt quá nó. Hãy nhớ rằng, các lập trình viên C++ không chỉ lười biếng (mà là sự thật là tôi thừa nhận), nhưng chúng cũng thông minh.

0

Làm thế nào về điều này:

for(sqrti = 1; sqrti <= 11112; sqrti++) { 
    int nexti = (1+sqrti)*(1+sqrti) 
    for (i = sqrti*sqrti; i < nexti; i++) 
    { 
    int isprime = 1; 
    for(j = 2; j < sqrti; j++) 
     if(!(i%j)) { 
     isprime = 0; 
     break; 
    } 
    } 

} countprimes + = isPrime; }