C # vs C - Hiệu suất khác biệt lớn


94

Tôi đang tìm thấy sự khác biệt lớn về hiệu suất giữa các mã tương tự trong C anc C #.

Mã C là:

#include <stdio.h>
#include <time.h>
#include <math.h>

main()
{
    int i;
    double root;

    clock_t start = clock();
    for (i = 0 ; i <= 100000000; i++){
        root = sqrt(i);
    }
    printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);   

}

Và C # (ứng dụng bảng điều khiển) là:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime startTime = DateTime.Now;
            double root;
            for (int i = 0; i <= 100000000; i++)
            {
                root = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds/1000));
        }
    }
}

Với đoạn mã trên, C # hoàn thành trong 0,328125 giây (phiên bản phát hành) và C mất 11,14 giây để chạy.

C đang được biên dịch thành một cửa sổ thực thi bằng mingw.

Tôi luôn giả định rằng C / C ++ nhanh hơn hoặc ít nhất là có thể so sánh với C # .net. Chính xác thì điều gì làm cho C chạy chậm hơn 30 lần?

CHỈNH SỬA: Có vẻ như trình tối ưu hóa C # đã loại bỏ gốc vì nó không được sử dụng. Tôi đã thay đổi phép gán gốc thành root + = và in ra tổng số ở cuối. Tôi cũng đã biên dịch C bằng cách sử dụng cl.exe với cờ / O2 được đặt cho tốc độ tối đa.

Kết quả bây giờ là: 3,75 giây cho C 2,61 giây cho C #

C vẫn mất nhiều thời gian hơn, nhưng điều này có thể chấp nhận được


18
Tôi khuyên bạn nên sử dụng Đồng hồ dừng thay vì chỉ DateTime.
Alex Fort

2
Cờ trình biên dịch nào? Cả hai đều được biên dịch với tính năng tối ưu hóa được bật?
jalf

2
Còn khi bạn sử dụng -ffast-math với trình biên dịch C ++ thì sao?
Dan McClain

10
Thật là một câu hỏi hấp dẫn!
Robert S.

4
Có thể hàm sqrt trong C không tốt như trong C #. Sau đó, nó sẽ không phải là một vấn đề với C, nhưng với thư viện gắn liền với nó. Thử một số phép tính không có hàm toán học.
klew

Câu trả lời:


61

Vì bạn không bao giờ sử dụng 'root', trình biên dịch có thể đã xóa lệnh gọi để tối ưu hóa phương pháp của bạn.

Bạn có thể cố gắng tích lũy các giá trị căn bậc hai vào một bộ tích lũy, in nó ra ở cuối phương pháp và xem điều gì đang xảy ra.

Chỉnh sửa: xem câu trả lời của Jalf bên dưới


1
Một thử nghiệm nhỏ cho thấy đây không phải là trường hợp. Mã cho vòng lặp được tạo, mặc dù có lẽ thời gian chạy đủ thông minh để bỏ qua nó. Thậm chí tích lũy, C # vẫn còn nhịp đập quần của C.
Dana

3
Có vẻ như vấn đề là ở đầu bên kia. C # hoạt động hợp lý trong mọi trường hợp. Mã C của ông dường như được biên dịch mà không cần tối ưu hóa
jalf

2
Rất nhiều bạn đang thiếu điểm ở đây. Tôi đã đọc nhiều trường hợp tương tự trong đó c # hoạt động tốt hơn c / c ++ và luôn luôn bác bỏ là sử dụng một số tối ưu hóa cấp chuyên gia. 99% lập trình viên không có kiến ​​thức để sử dụng các kỹ thuật tối ưu hóa như vậy chỉ để mã của họ chạy nhanh hơn một chút so với mã c #. Các trường hợp sử dụng cho c / c ++ đang thu hẹp.

167

Bạn phải so sánh các bản dựng gỡ lỗi. Tôi vừa biên dịch mã C của bạn, và nhận được

Time elapsed: 0.000000

Nếu bạn không bật tối ưu hóa, bất kỳ điểm chuẩn nào bạn thực hiện đều hoàn toàn vô giá trị. (Và nếu bạn bật tối ưu hóa, vòng lặp sẽ bị tối ưu hóa. Vì vậy, mã điểm chuẩn của bạn cũng bị sai sót. Bạn cần buộc nó chạy vòng lặp, thường bằng cách tổng hợp kết quả hoặc tương tự và in ra ở cuối)

Có vẻ như những gì bạn đang đo về cơ bản là "trình biên dịch nào chèn nhiều chi phí gỡ lỗi nhất". Và hóa ra câu trả lời là C. Nhưng điều đó không cho chúng ta biết chương trình nào là nhanh nhất. Bởi vì khi bạn muốn tốc độ, bạn kích hoạt tính năng tối ưu hóa.

Nhân tiện, bạn sẽ đỡ phải đau đầu về lâu dài nếu từ bỏ quan niệm ngôn ngữ "nhanh hơn" nhau. C # không có tốc độ nào hơn tiếng Anh.

Có một số thứ trong ngôn ngữ C sẽ hiệu quả ngay cả trong một trình biên dịch không tối ưu hóa ngây thơ, và có những thứ khác phụ thuộc nhiều vào trình biên dịch để tối ưu hóa mọi thứ. Và tất nhiên, C # hay bất kỳ ngôn ngữ nào khác cũng vậy.

Tốc độ thực thi được xác định bởi:

  • nền tảng bạn đang chạy (Hệ điều hành, phần cứng, phần mềm khác đang chạy trên hệ thống)
  • trình biên dịch
  • mã nguồn của bạn

Một trình biên dịch C # tốt sẽ mang lại mã hiệu quả. Một trình biên dịch C không tốt sẽ tạo ra mã chậm. Điều gì về trình biên dịch C tạo ra mã C #, sau đó bạn có thể chạy thông qua trình biên dịch C #? Nó sẽ chạy nhanh như thế nào? Ngôn ngữ không có tốc độ. Mã của bạn không.


Đọc thêm rất nhiều điều thú vị ở đây: blog.msdn.com/ricom/archive/2005/05/10/416151.aspx
Daniel Earwicker

18
Câu trả lời tốt, nhưng tôi không đồng ý về tốc độ ngôn ngữ, ít nhất là trong phép loại suy: Người ta thấy rằng tiếng Welsch là ngôn ngữ chậm hơn hầu hết vì tần số cao của các nguyên âm dài. Ngoài ra, mọi người nhớ từ (và danh sách từ) tốt hơn nếu họ nói nhanh hơn. web.missouri.edu/~cowann/docs/articles/before%201993/... en.wikipedia.org/wiki/Vowel_length en.wikipedia.org/wiki/Welsh_language
exceptionerror

1
Điều đó không phụ thuộc vào những gì bạn đang nói ở Welsch? Tôi thấy không chắc rằng mọi thứ đều chậm hơn.
jalf

5
++ Này các bạn, đừng lạc vào đây. Nếu cùng một chương trình chạy bằng ngôn ngữ này nhanh hơn ngôn ngữ khác thì đó là do mã hợp ngữ khác nhau được tạo ra. Trong ví dụ cụ thể này, 99% thời gian trở lên sẽ chuyển sang trạng thái thả nổi i, và sqrtđó là những gì đang được đo lường.
Mike Dunlavey

116

Tôi sẽ giữ nó ngắn gọn, nó đã được đánh dấu là đã trả lời. C # có lợi thế lớn là có một mô hình dấu chấm động được xác định rõ. Điều đó chỉ xảy ra để khớp với chế độ hoạt động gốc của tập lệnh FPU và SSE trên bộ xử lý x86 và x64. Không phải ngẫu nhiên ở đó. JITter biên dịch Math.Sqrt () thành một số hướng dẫn nội tuyến.

Native C / C ++ bị hạn chế với nhiều năm khả năng tương thích ngược. Các tùy chọn biên dịch / fp: precision, / fp: fast và / fp: nghiêm ngặt là dễ thấy nhất. Theo đó, nó phải gọi một hàm CRT thực hiện hàm sqrt () và kiểm tra các tùy chọn dấu chấm động đã chọn để điều chỉnh kết quả. Thật chậm.


66
Đây là một niềm tin kỳ lạ đối với các lập trình viên C ++, họ dường như nghĩ rằng mã máy được tạo bởi C # bằng cách nào đó khác với mã máy được tạo bởi trình biên dịch gốc. Chỉ có một loại. Bất kể bạn sử dụng công tắc trình biên dịch gcc nào hoặc lắp ráp nội tuyến mà bạn viết, vẫn chỉ có một lệnh FSQRT. Nó không phải lúc nào cũng nhanh hơn vì ngôn ngữ mẹ đẻ tạo ra nó, cpu không quan tâm.
Hans Passant

16
Đó là những gì pre-jitting với ngen.exe giải quyết. Chúng ta đang nói về C #, không phải Java.
Hans Passant

20
@ user877329 - thật không? Chà.
Andras Zoltan

7
Không, jitter x64 sử dụng SSE. Math.Sqrt () được dịch sang lệnh mã máy sqrtsd.
Hans Passant 19/10/12

6
Mặc dù về mặt kỹ thuật nó không phải là sự khác biệt giữa các ngôn ngữ, .net JITter thực hiện tối ưu hóa khá hạn chế so với trình biên dịch C / C ++ thông thường. Một trong những hạn chế lớn nhất là thiếu hỗ trợ SIMD khiến mã thường chậm hơn khoảng 4 lần. Không tiết lộ nhiều bản chất cũng có thể là một vấn đề lớn, nhưng điều đó phụ thuộc rất nhiều vào những gì bạn đang làm.
CodesInChaos

57

Tôi là một nhà phát triển C ++ và C #. Tôi đã phát triển các ứng dụng C # kể từ bản beta đầu tiên của .NET framework và tôi đã có hơn 20 năm kinh nghiệm trong việc phát triển các ứng dụng C ++. Thứ nhất, mã C # sẽ KHÔNG BAO GIỜ nhanh hơn ứng dụng C ++, nhưng tôi sẽ không thảo luận dài dòng về mã được quản lý, cách nó hoạt động, lớp inter-op, nội bộ quản lý bộ nhớ, hệ thống kiểu động và bộ thu gom rác. Tuy nhiên, hãy để tôi tiếp tục bằng cách nói rằng các điểm chuẩn được liệt kê ở đây đều cho kết quả KHÔNG ĐÚNG.

Để tôi giải thích: Điều đầu tiên chúng ta cần xem xét là trình biên dịch JIT cho C # (.NET Framework 4). Bây giờ JIT tạo mã gốc cho CPU bằng cách sử dụng các thuật toán tối ưu hóa khác nhau (có xu hướng tích cực hơn trình tối ưu hóa C ++ mặc định đi kèm với Visual Studio) và tập lệnh được sử dụng bởi trình biên dịch .NET JIT là sự phản ánh gần gũi hơn với CPU thực tế trên máy để có thể thực hiện một số thay thế nhất định trong mã máy để giảm chu kỳ xung nhịp và cải thiện tốc độ truy cập trong bộ đệm đường ống CPU và tạo ra các tối ưu hóa siêu phân luồng hơn nữa, chẳng hạn như chúng tôi sắp xếp lại lệnh và cải tiến liên quan đến dự đoán nhánh.

Điều này có nghĩa là trừ khi bạn biên dịch ứng dụng C ++ của mình bằng cách sử dụng các tham số chính xác cho bản dựng RELEASE (không phải bản dựng GỬI) thì ứng dụng C ++ của bạn có thể hoạt động chậm hơn ứng dụng dựa trên C # hoặc .NET tương ứng. Khi chỉ định các thuộc tính dự án trên ứng dụng C ++ của bạn, hãy đảm bảo bạn bật "tối ưu hóa hoàn toàn" và "ưu tiên mã nhanh". Nếu bạn có máy 64 bit, bạn PHẢI chỉ định tạo x64 làm nền tảng đích, nếu không mã của bạn sẽ được thực thi thông qua lớp con chuyển đổi (WOW64) sẽ làm giảm đáng kể hiệu suất.

Khi bạn thực hiện tối ưu hóa chính xác trong trình biên dịch, tôi nhận được .72 giây cho ứng dụng C ++ và 1,16 giây cho ứng dụng C # (cả hai đều trong bản dựng phát hành). Vì ứng dụng C # rất cơ bản và phân bổ bộ nhớ được sử dụng trong vòng lặp trên ngăn xếp chứ không phải trên đống, nên nó thực sự hoạt động tốt hơn rất nhiều so với một ứng dụng thực liên quan đến các đối tượng, tính toán nặng và với các tập dữ liệu lớn hơn. Vì vậy, những con số được cung cấp là những con số lạc quan, thiên về C # và .NET framework. Ngay cả với sự thiên vị này, ứng dụng C ++ hoàn thành chỉ trong hơn một nửa thời gian so với ứng dụng C # tương đương. Hãy nhớ rằng trình biên dịch C ++ của Microsoft mà tôi đã sử dụng không có đúng đường dẫn và tối ưu hóa siêu phân luồng (sử dụng WinDBG để xem hướng dẫn lắp ráp).

Bây giờ nếu chúng ta sử dụng trình biên dịch Intel (nhân tiện là bí mật ngành để tạo các ứng dụng hiệu suất cao trên bộ xử lý AMD / Intel), thì cùng một mã thực thi trong .54 giây đối với tệp thực thi C ++ so với .72 giây khi sử dụng Microsoft Visual Studio 2010 Vì vậy, cuối cùng, kết quả cuối cùng là .54 giây cho C ++ và 1.16 giây cho C #. Vì vậy, mã do trình biên dịch .NET JIT tạo ra mất thời gian dài hơn 214% so với mã thực thi C ++. Phần lớn thời gian trong .54 giây là để lấy thời gian từ hệ thống chứ không phải trong chính vòng lặp!

Điều còn thiếu trong số liệu thống kê là thời gian khởi động và dọn dẹp không được bao gồm trong thời gian. Các ứng dụng C # có xu hướng dành nhiều thời gian cho việc khởi động và kết thúc hơn các ứng dụng C ++. Lý do đằng sau điều này là phức tạp và liên quan đến các quy trình xác thực mã thời gian chạy .NET và hệ thống con quản lý bộ nhớ thực hiện rất nhiều công việc ở phần đầu (và do đó là phần cuối) của chương trình để tối ưu hóa việc cấp phát bộ nhớ và rác. người thu tiền.

Khi đo hiệu suất của C ++ và .NET IL, điều quan trọng là phải xem mã lắp ráp để đảm bảo rằng TẤT CẢ các phép tính đều ở đó. Những gì tôi nhận thấy là nếu không đặt một số mã bổ sung vào C #, hầu hết mã trong các ví dụ trên đã thực sự bị xóa khỏi hệ nhị phân. Đây cũng là trường hợp của C ++ khi bạn sử dụng trình tối ưu hóa tích cực hơn, chẳng hạn như trình tối ưu hóa đi kèm với trình biên dịch Intel C ++. Kết quả tôi cung cấp ở trên là đúng 100% và được xác nhận ở cấp độ lắp ráp.

Vấn đề chính của rất nhiều diễn đàn trên internet mà rất nhiều người mới nghe Microsoft tuyên truyền về marketing mà không hiểu công nghệ và tuyên bố sai rằng C # nhanh hơn C ++. Tuyên bố là về lý thuyết, C # nhanh hơn C ++ vì trình biên dịch JIT có thể tối ưu hóa mã cho CPU. Vấn đề với lý thuyết này là có rất nhiều hệ thống ống nước tồn tại trong .NET framework làm chậm hiệu suất; hệ thống ống nước không tồn tại trong ứng dụng C ++. Hơn nữa, một nhà phát triển có kinh nghiệm sẽ biết trình biên dịch phù hợp để sử dụng cho nền tảng nhất định và sử dụng các cờ thích hợp khi biên dịch ứng dụng. Trên nền tảng Linux hoặc mã nguồn mở, đây không phải là vấn đề vì bạn có thể phân phối mã nguồn của mình và tạo tập lệnh cài đặt biên dịch mã bằng cách sử dụng tối ưu hóa thích hợp. Trên cửa sổ hoặc nền tảng mã nguồn đóng, bạn sẽ phải phân phối nhiều tệp thực thi, mỗi tệp có các tối ưu hóa cụ thể. Các tệp nhị phân cửa sổ sẽ được triển khai dựa trên CPU được phát hiện bởi trình cài đặt msi (sử dụng các hành động tùy chỉnh).


22
1. Microsoft chưa bao giờ đưa ra những tuyên bố đó về việc C # nhanh hơn. Tuyên bố của họ là tốc độ của nó chiếm khoảng 90%, phát triển nhanh hơn (và do đó có nhiều thời gian hơn để điều chỉnh) và ít lỗi hơn do an toàn về bộ nhớ và kiểu. Tất cả đều đúng (tôi có 20 năm trong C ++ và 10 năm trong C #) 2. Hiệu suất khởi động là vô nghĩa trong hầu hết các trường hợp. 3. Ngoài ra còn có các trình biên dịch C # nhanh hơn như LLVM (vì vậy việc đưa Intel ra mắt không phải là Táo khuyết)
ben

13
Hiệu suất khởi động không phải là vô nghĩa. Nó rất quan trọng trong hầu hết các ứng dụng dựa trên web doanh nghiệp, đó là lý do tại sao Microsoft giới thiệu các trang web được tải trước (tự động khởi động) trong .NET 4.0. Khi nhóm ứng dụng được tái chế thỉnh thoảng, lần đầu tiên mỗi lần tải trang sẽ gây thêm độ trễ đáng kể cho các trang phức tạp và gây ra thời gian chờ trên trình duyệt.
Richard,

8
Microsoft đã đưa ra tuyên bố về hiệu suất của .NET nhanh hơn trong tài liệu tiếp thị trước đó. Họ cũng đưa ra nhiều tuyên bố khác nhau về việc bộ thu gom rác có ít hoặc không ảnh hưởng đến hiệu suất. Một số tuyên bố này đã đưa nó vào các cuốn sách khác nhau (trên ASP.NET và .NET) trong các ấn bản trước đó của họ. Mặc dù Microsoft không nói cụ thể rằng ứng dụng C # của bạn sẽ nhanh hơn ứng dụng C ++ của bạn, nhưng họ có thể quét các nhận xét chung chung và khẩu hiệu tiếp thị như "Just-In-Time Means Run-It-Fast" ( msdn.microsoft.com/ vi-us / library / ms973894.aspx ).
Richard,

71
-1, rant này là đầy đủ các báo cáo không chính xác và gây hiểu lầm như Whopper rõ ràng "mã C # sẽ KHÔNG BAO GIỜ được nhanh hơn so với một ứng dụng C ++"
BCoates

32
-1. Bạn nên đọc trận chiến giữa C # vs C của Rico Mariani vs Raymond Chen: blog.msdn.com/b/ricom/archive/2005/05/16/418051.aspx . Tóm lại: một trong những người thông minh nhất của Microsoft đã phải mất rất nhiều tối ưu hóa để làm cho phiên bản C nhanh hơn phiên bản C # đơn giản.
Rolf Bjarne Kvinge

10

dự đoán đầu tiên của tôi là tối ưu hóa trình biên dịch bởi vì bạn không bao giờ sử dụng root. Bạn chỉ cần gán nó, sau đó ghi đè nó nhiều lần.

Chỉnh sửa: chết tiệt, đánh bại 9 giây!


2
Tôi nói bạn đúng. Biến thực tế bị ghi đè và không bao giờ được sử dụng ngoài biến đó. Csc rất có thể sẽ chỉ bỏ qua toàn bộ vòng lặp trong khi trình biên dịch c ++ có thể để lại nó. Một bài kiểm tra chính xác hơn sẽ là tích lũy kết quả và sau đó in kết quả đó ra ở cuối. Ngoài ra, người ta không nên mã hóa cứng giá trị hạt giống, mà nên để nó được người dùng xác định. Điều này sẽ không cung cấp cho trình biên dịch c # bất kỳ chỗ trống nào để bỏ sót nội dung.

7

Để xem liệu vòng lặp có đang được tối ưu hóa hay không, hãy thử thay đổi mã của bạn thành

root += Math.Sqrt(i);

ans tương tự trong mã C, và sau đó in giá trị của gốc bên ngoài vòng lặp.


6

Có thể trình biên dịch c # nhận thấy bạn không sử dụng root ở bất kỳ đâu, vì vậy nó chỉ bỏ qua toàn bộ vòng lặp for. :)

Đó có thể không phải là trường hợp, nhưng tôi nghi ngờ bất kể nguyên nhân là gì, nó phụ thuộc vào việc triển khai trình biên dịch. Hãy thử biên dịch chương trình C của bạn bằng trình biên dịch Microsoft (cl.exe, có sẵn như một phần của win32 sdk) với tối ưu hóa và chế độ Phát hành. Tôi cá là bạn sẽ thấy một sự cải tiến hoàn hảo so với trình biên dịch khác.

CHỈNH SỬA: Tôi không nghĩ rằng trình biên dịch chỉ có thể tối ưu hóa vòng lặp for, bởi vì nó sẽ phải biết rằng Math.Sqrt () không có bất kỳ tác dụng phụ nào.


2
Có lẽ nó biết điều đó.

2
@Neil, @jeff: Đồng ý, nó có thể biết điều đó khá dễ dàng. Tùy thuộc vào việc triển khai, phân tích tĩnh trên Math.Sqrt () có thể không quá khó, mặc dù tôi không chắc những gì tối ưu hóa được thực hiện cụ thể.
John Feminella

5

Dù thời gian khác nhau. có thể là, "thời gian đã trôi qua" đó không hợp lệ. Nó sẽ chỉ là một chương trình hợp lệ nếu bạn có thể đảm bảo rằng cả hai chương trình đều chạy trong các điều kiện giống hệt nhau.

Có lẽ bạn nên thử một chiến thắng. tương đương với $ / usr / bin / time my_cprog; / usr / bin / time my_csprog


1
Tại sao điều này bị phản đối? Có ai cho rằng ngắt và chuyển mạch ngữ cảnh không ảnh hưởng đến hiệu suất không? Có ai có thể đưa ra giả định về việc bỏ sót TLB, hoán đổi trang, v.v. không?
Tom

5

Tôi đã kết hợp (dựa trên mã của bạn) hai bài kiểm tra có thể so sánh được trong C và C #. Hai mảng này viết một mảng nhỏ hơn bằng cách sử dụng toán tử mô-đun để lập chỉ mục (nó thêm một chút chi phí, nhưng này, chúng tôi đang cố gắng so sánh hiệu suất [ở mức thô]).

Mã C:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

void main()
{
    int count = (int)1e8;
    int subcount = 1000;
    double* roots = (double*)malloc(sizeof(double) * subcount);
    clock_t start = clock();
    for (int i = 0 ; i < count; i++)
    {
        roots[i % subcount] = sqrt((double)i);
    }
    clock_t end = clock();
    double length = ((double)end - start) / CLOCKS_PER_SEC;
    printf("Time elapsed: %f\n", length);
}

Trong C #:

using System;

namespace CsPerfTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = (int)1e8;
            int subcount = 1000;
            double[] roots = new double[subcount];
            DateTime startTime = DateTime.Now;
            for (int i = 0; i < count; i++)
            {
                roots[i % subcount] = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds / 1000));
        }
    }
}

Các bài kiểm tra này ghi dữ liệu vào một mảng (vì vậy, thời gian chạy .NET không được phép xử lý sqrt op) mặc dù mảng nhỏ hơn đáng kể (không muốn sử dụng quá nhiều bộ nhớ). Tôi đã biên dịch chúng trong cấu hình phát hành và chạy chúng từ bên trong cửa sổ bảng điều khiển (thay vì bắt đầu thông qua VS).

Trên máy tính của tôi, chương trình C # thay đổi từ 6,2 đến 6,9 giây, trong khi phiên bản C thay đổi trong khoảng 6,9 đến 7,1.


5

Nếu bạn chỉ thực hiện một bước mã ở cấp lắp ráp, bao gồm cả bước qua quy trình căn bậc hai, bạn có thể sẽ nhận được câu trả lời cho câu hỏi của mình.

Không cần phải đoán già đoán non.


Tôi muốn biết làm thế nào để làm điều này
Josh Stodola

Phụ thuộc vào IDE hoặc trình gỡ lỗi của bạn. Ngắt khi bắt đầu pgm. Hiển thị cửa sổ tháo gỡ và bắt đầu bước đơn. Nếu sử dụng GDB, có các lệnh để thực hiện từng bước một.
Mike Dunlavey

Bây giờ đó là một mẹo hay, điều này giúp người ta hiểu thêm những gì đang thực sự diễn ra ở đó. Điều đó cũng hiển thị các tối ưu hóa JIT như lệnh gọi nội tuyến và đuôi phải không?
gjvdkamp,

FYI: đối với tôi, điều này cho thấy VC ++ sử dụng fadd và fsqrt trong khi C # sử dụng cvtsi2sd và sqrtsd mà tôi hiểu là hướng dẫn SSE2 và nhanh hơn đáng kể khi được hỗ trợ.
danio

2

Yếu tố khác có thể là một vấn đề ở đây là trình biên dịch C biên dịch thành mã gốc chung cho họ bộ xử lý mà bạn nhắm mục tiêu, trong khi MSIL được tạo khi bạn biên dịch mã C # sau đó được biên dịch JIT để nhắm mục tiêu chính xác bộ xử lý mà bạn có. những tối ưu có thể có. Vì vậy, mã gốc được tạo từ C # có thể nhanh hơn đáng kể so với C.


Về lý thuyết, có. Trong thực tế, điều đó hầu như không bao giờ tạo ra sự khác biệt có thể đo lường được. Có lẽ một hoặc hai phần trăm, nếu bạn may mắn.
jalf

hoặc - nếu bạn có một số loại mã nhất định sử dụng các tiện ích mở rộng không có trong danh sách được phép cho bộ xử lý 'chung'. Những thứ như hương vị SSE. Hãy thử với mục tiêu bộ xử lý được đặt cao hơn, để xem bạn nhận được sự khác biệt nào.
gbjbaanb

1

Đối với tôi, dường như điều này không liên quan gì đến bản thân các ngôn ngữ, mà nó liên quan đến các triển khai khác nhau của hàm căn bậc hai.


Tôi thực sự nghi ngờ việc triển khai sqrt khác nhau sẽ gây ra sự khác biệt nhiều như vậy.
Alex Fort

Đặc biệt vì ngay cả trong C #, hầu hết các hàm toán học vẫn được coi là hiệu suất quan trọng và được thực hiện như vậy.
Matthew Olenik

fsqrt là một hướng dẫn bộ xử lý IA-32, vì vậy hiện nay việc triển khai ngôn ngữ là không thích hợp.
Không chắc

Bước vào chức năng sqrt của MSVC với trình gỡ lỗi. Nó làm được nhiều việc hơn là chỉ thực hiện lệnh fsqrt.
bk1e

1

Trên thực tế, vòng lặp KHÔNG được tối ưu hóa. Tôi đã biên dịch mã của John và kiểm tra .exe kết quả. Nội dung của vòng lặp như sau:

 IL_0005:  stloc.0
 IL_0006:  ldc.i4.0
 IL_0007:  stloc.1
 IL_0008:  br.s       IL_0016
 IL_000a:  ldloc.1
 IL_000b:  conv.r8
 IL_000c:  call       float64 [mscorlib]System.Math::Sqrt(float64)
 IL_0011:  pop
 IL_0012:  ldloc.1
 IL_0013:  ldc.i4.1
 IL_0014:  add
 IL_0015:  stloc.1
 IL_0016:  ldloc.1
 IL_0017:  ldc.i4     0x5f5e100
 IL_001c:  ble.s      IL_000a

Trừ khi thời gian chạy đủ thông minh để nhận ra vòng lặp không có gì và bỏ qua nó?

Chỉnh sửa: Thay đổi C # thành:

 static void Main(string[] args)
 {
      DateTime startTime = DateTime.Now;
      double root = 0.0;
      for (int i = 0; i <= 100000000; i++)
      {
           root += Math.Sqrt(i);
      }
      System.Console.WriteLine(root);
      TimeSpan runTime = DateTime.Now - startTime;
      Console.WriteLine("Time elapsed: " +
          Convert.ToString(runTime.TotalMilliseconds / 1000));
 }

Kết quả trong thời gian trôi qua (trên máy của tôi) sẽ từ 0,047 đến 2,17. Nhưng đó chỉ là chi phí của việc thêm 100 triệu toán tử bổ sung?


3
Nhìn vào IL không cho bạn biết nhiều về tối ưu hóa bởi vì mặc dù trình biên dịch C # thực hiện một số việc như liên tục gấp và xóa mã chết, IL sau đó sẽ tiếp nhận và thực hiện phần còn lại tại thời gian tải.
Daniel Earwicker

Đó là những gì tôi nghĩ có thể là trường hợp. Tuy nhiên, ngay cả khi buộc nó hoạt động, nó vẫn nhanh hơn 9 giây so với phiên bản C. (Tôi hoàn toàn không mong đợi điều đó)
Dana
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.