Trình biên dịch Fortran có thực sự tạo mã nhanh hơn trình biên dịch C không?


17

Khi tôi học đại học, tôi thường nghe ý tưởng rằng trình biên dịch Fortran tạo mã nhanh hơn trình biên dịch C cho một chương trình tương đương.

Lý do chính đã đi như thế này: trình biên dịch Fortran phát ra trung bình 1,1 lệnh của bộ xử lý trên mỗi dòng mã, trong khi trình biên dịch C phát ra trung bình 1,6 lệnh của bộ xử lý trên mỗi dòng mã - Tôi không nhớ các con số chính xác nhưng ý tưởng là trình biên dịch C phát ra nhiều mã máy hơn và do đó tạo ra các chương trình chậm hơn.

Làm thế nào hợp lệ là so sánh như vậy? Chúng ta có thể nói rằng trình biên dịch Fortran tạo ra các chương trình nhanh hơn trình biên dịch C hoặc ngược lại và tại sao sự khác biệt này tồn tại?


19
Điều đó có thể chỉ đơn giản có nghĩa là các chương trình Fortran dài dòng hơn C ... Một so sánh có ý nghĩa chỉ có thể được thực hiện bằng cách thực hiện cùng chức năng trong cả hai ngôn ngữ và so sánh mã máy kết quả (kích thước và tốc độ).
Péter Török

Ngoài ra, mã được tạo có hỗ trợ thực hiện song song không?

@ Péter Török, nó đơn giản có nghĩa là, BLAS và LAPACK trong Fortran được sử dụng để thực hiện tốt hơn nhiều so với bất kỳ cổng C / C ++ nào của họ. Bây giờ khoảng cách đang thu hẹp nhanh chóng.
SK-logic

6
Bạn chỉ có thể lập luận rằng một trình biên dịch tạo mã nhanh hơn nếu bạn có chương trình tương đương 100% bằng cả hai ngôn ngữ, được viết bởi các chuyên gia biết trình biên dịch của họ và người có thể giải thích cho hiệu suất.
Falcon

Fortran trước đây không hỗ trợ đệ quy và do đó không nhất thiết phải đẩy các đối số gọi hàm lên ngăn xếp vì sẽ có một không gian được phân bổ tĩnh cho các đối số của mỗi chức năng. Đây là một trong những lý do tại sao nó có thể đã nhanh hơn. Tôi đoán bạn có thể tìm thấy một câu trả lời đầy đủ hơn ở đây: amazon.com/Programming-L Language
Pragmatics-Third-Edition/dp/ Kẻ

Câu trả lời:


36

IIRC một trong những lý do chính khiến Fortran được cho là nhanh hơn là không có bí danh con trỏ , vì vậy họ có thể sử dụng tối ưu hóa mà trình biên dịch C không thể sử dụng:

Trong FORTRAN, các đối số hàm có thể không bí danh lẫn nhau và trình biên dịch giả định rằng chúng không có. Điều này cho phép tối ưu hóa tuyệt vời và là một lý do chính cho danh tiếng của FORTRAN là ngôn ngữ nhanh. (Lưu ý rằng hiện tượng răng cưa vẫn có thể xảy ra trong hàm FORTRAN. Ví dụ: nếu A là một mảng và i và j là các chỉ số xảy ra có cùng giá trị, thì A [i] và A [j] là hai tên khác nhau cho May mắn thay, vì mảng cơ sở phải có cùng tên, phân tích chỉ mục có thể được thực hiện để xác định các trường hợp trong đó A [i] và A [j] không thể bí danh.)

Nhưng tôi đồng ý với những người khác ở đây: So sánh số lượng trung bình của các hướng dẫn trình biên dịch được tạo cho một dòng mã là hoàn toàn vô nghĩa. Ví dụ, lõi x86 hiện đại có thể thực thi song song hai hướng dẫn nếu chúng không truy cập vào cùng các thanh ghi. Vì vậy, về mặt lý thuyết, bạn có thể tăng hiệu suất 100% cho cùng một bộ theo hướng dẫn chỉ bằng cách sắp xếp lại chúng . Trình biên dịch tốt cũng sẽ thường tạo ra nhiều hướng dẫn lắp ráp hơn để có được mã nhanh hơn (nghĩ rằng không kiểm soát vòng lặp, nội tuyến). Tổng số hướng dẫn trình biên dịch chương trình nói rất ít về hiệu suất của một đoạn mã.


Một lý do khác để tối ưu hóa tốt hơn là hỗ trợ riêng cho các số phức.
SK-logic

Chắc chắn đúng cho Fortran IV hoặc hơn. Không chắc chắn nếu các FORTRAN hiện đại vẫn không có con trỏ, meory năng động, v.v.
Ingo

2
Đó là cùng một lý do chúng tôi thường giảm xuống một chút lắp ráp nội tuyến khi phát triển C và C ++ trong ngành công nghiệp trò chơi. Mọi người có thể yêu cầu thường xuyên như họ muốn rằng "trình biên dịch có thể tối ưu hóa tốt hơn con người viết lắp ráp", thực tế là, răng cưa con trỏ có nghĩa là họ thường không thể . Mã chúng ta có thể viết bằng tay sẽ là bất hợp pháp về mặt kỹ thuật để trình biên dịch phát ra, vì nó không làm gì về răng cưa con trỏ.
Carson63000

5
restrictTừ khóa của C cho phép tác giả của một hàm xác định rằng một con trỏ không có bí danh. Điều này có đủ để giải quyết sự khác biệt, hoặc có nhiều hơn cho nó?
bk.

@bk.: "Hạn chế" các cuộc tấn công của C "một nửa vấn đề"; nó có thể nói rằng một con trỏ cụ thể sẽ không bí danh bất kỳ thứ gì khác trong vòng đời của nó, nhưng không có cách nào để nói với trình biên dịch rằng một đối tượng có địa chỉ được truyền tới một hàm sẽ không bị bí danh bởi bất cứ điều gì khi hàm đó trả về.
supercat

8

Hoàn toàn so sánh không hợp lệ.

Đầu tiên, như @ Péter Török chỉ ra, trước tiên bạn phải so sánh số lượng dòng trong các chương trình tương đương từ Fortran và C để điều này thậm chí là so sánh hợp lệ về số lượng dòng được sản xuất.

Thứ hai, ít dòng mã hơn không phải lúc nào cũng bằng các chương trình nhanh hơn . Không phải tất cả các lệnh máy đều có cùng số chu kỳ để thực thi , nhưng bạn cũng gặp các vấn đề khác như truy cập bộ nhớ , bộ nhớ đệm , v.v.

Trên hết, việc chạy mã dài có thể nhanh hơn vì nó dẫn đến số lượng dòng thực thi thấp hơn (nghĩa là Đếm dòng! = Đếm dòng đã thực hiện ).


5

Dan là chính xác, chương trình dài hơn không có nghĩa là chương trình chậm hơn. Nó phụ thuộc rất nhiều vào những gì họ đang làm.

Tôi không phải là chuyên gia về Fortran, tôi biết một chút. So sánh chúng, tôi sẽ nghĩ rằng C được viết tốt sẽ có hiệu suất tốt hơn nhiều với các cấu trúc dữ liệu và chức năng phức tạp hơn Fortran. Ai đó (xin vui lòng) sửa lỗi cho tôi nếu tôi sai ở đây, nhưng tôi nghĩ Fortran có phần ở mức 'thấp hơn' so với C. Nếu vậy, tôi chắc chắn rằng một số vấn đề sẽ xuất hiện nhanh hơn trên Fortran.

Một điều nữa, thoạt nhìn tôi nghĩ bạn đang hỏi liệu trình biên dịch có nhanh hơn không. Tôi thực sự nghĩ rằng Fortran thường sẽ biên dịch nhanh hơn với số lượng mã tương tự, nhưng chương trình kết quả và cách nó chạy sẽ là một câu chuyện khác. Nó chỉ đơn giản hơn để phân tích thông qua.


2
Nếu bạn đang sử dụng các cấu trúc dữ liệu phức tạp thì FORTRAN có lẽ là lựa chọn sai. FORTRAN được tối ưu hóa để thực hiện crunch số đơn giản rất nhanh.
Zachary K

4

Tôi nghĩ một phần của nó là trình biên dịch FORTRAN được thiết kế để thực hiện một số loại toán rất nhanh. Đó là lý do tại sao mọi người sử dụng FORTRAN, để thực hiện các phép tính nhanh nhất có thể


4

Tuyên bố có thể đúng trong những ngày xưa (khoảng cuối thập niên 70) khi C còn ở giai đoạn đầu và Fortran được tất cả các nhà sản xuất lớn hỗ trợ và được tối ưu hóa cao. Fortrans ban đầu dựa trên kiến ​​trúc IBM nên những thứ đơn giản như số học nếu chắc chắn sẽ là một tuyên bố trên mỗi hướng dẫn lắp ráp. Điều này đúng với các máy cũ hơn như Data General và Prime, đã có 3 bước nhảy. Điều này không hoạt động trên các bộ hướng dẫn hiện đại không có bước nhảy 3 chiều.

Các dòng mã không bằng các câu lệnh của mã. Các phiên bản trước của Fortran chỉ cho phép một tuyên bố trên mỗi dòng. Các phiên bản sau của Fortran có thể nhận nhiều báo cáo trên mỗi dòng. C có thể có nhiều câu lệnh trên mỗi dòng. Trên các trình biên dịch sản xuất nhanh hơn như IVF của Intel (trước đây là CVF, MS Powerstation) và Intel's C, thực sự không có sự khác biệt giữa hai trình biên dịch. Những trình biên dịch được tối ưu hóa cao.


4

FORTRAN kiểu cũ yêu cầu một lập trình viên muốn tạo một phần của mảng có sẵn cho một hàm cần thiết để chuyển một tham chiếu đến toàn bộ mảng, cùng với một hoặc nhiều giá trị số nguyên chỉ định chỉ mục bắt đầu và chỉ mục kết thúc hoặc số mục . C làm cho nó có thể đơn giản hóa việc này để chuyển một con trỏ đến phần bắt đầu của phần quan tâm cùng với số phần tử. Nói một cách trực tiếp, điều này sẽ làm cho mọi thứ nhanh hơn (vượt qua hai điều chứ không phải ba). Tuy nhiên, một cách gián tiếp, nó có thể làm mọi thứ chậm lại bằng cách hạn chế các loại tối ưu hóa mà trình biên dịch có thể thực hiện.

Hãy xem xét chức năng:

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

nếu một trình biên dịch biết rằng mỗi con trỏ sẽ xác định bắt đầu một mảng, thì nó có thể tạo mã sẽ hoạt động theo các phần tử của mảng song song hoặc theo bất kỳ thứ tự nào, vì với bất kỳ x! = y nào, hoạt động trên mệnh [x ] sẽ không ảnh hưởng đến src1 [y] cũng như src2 [y]. Ví dụ: trên một số hệ thống, trình biên dịch có thể được hưởng lợi từ việc tạo mã tương đương với:

void dif(float dest[], float src1[], float src2[], int n)
{
  int i=0;
  float t1a,t1b,t2a,t2b,tsa,tsb;
  if (n > 2)
  {
    n-=4;
    t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
    do
    {
      tsa = t1a-t2a;
      t1a = src1[n+1]; t2a = src2[n+1]; 
      tsb = t2b-t2b;
      dest[n+3] = tsa;
      t1b = src1[n]; t2b = src2[n]; 
      n-=2;
      dest[n+4] = tsb;
    } while(n >= 0);
    ... add some extra code to handle cleanup
  }
  else
    ... add some extra code to handle small values of n
}

Lưu ý rằng mọi thao tác tải hoặc tính toán một giá trị đều có ít nhất một thao tác nữa giữa nó và thao tác tiếp theo sử dụng giá trị đó. Một số bộ xử lý có thể chồng lấp quá trình xử lý các hoạt động khác nhau khi các điều kiện như vậy được đáp ứng, do đó cải thiện hiệu suất. Tuy nhiên, lưu ý rằng vì trình biên dịch C không có cách nào để biết rằng mã sẽ không được chuyển con trỏ đến các vùng chồng lấp một phần của một mảng chung, trình biên dịch C không thể thực hiện chuyển đổi ở trên. Trình biên dịch FORTRAN được cung cấp mã tương đương, tuy nhiên, có thể và đã thực hiện một chuyển đổi như vậy.

Trong khi một lập trình viên C có thể cố gắng đạt được hiệu năng tương đương bằng cách viết rõ ràng mã không kiểm soát vòng lặp và chồng chéo các hoạt động của các đường chuyền liền kề, mã đó có thể dễ dàng làm giảm hiệu suất nếu nó sử dụng quá nhiều biến tự động mà trình biên dịch phải "đổ" chúng ký ức. Trình tối ưu hóa của trình biên dịch FORTRAN có thể sẽ biết nhiều hơn một lập trình viên về các hình thức xen kẽ nào sẽ mang lại hiệu suất tối ưu trong một kịch bản nhất định và các quyết định như vậy thường được dành cho các trình biên dịch như vậy. Mặc dù C99 đã cố gắng cải thiện tình hình của C bằng cách thêm một restrictvòng loại, nhưng chỉ có thể được sử dụng ở đây nếu dest[]là một mảng riêng biệt từ cả hai src1[]src2[]nếu lập trình viên thêm các phiên bản riêng của vòng lặp để xử lý các trường hợp trong đó tất cảdest đã rời nhau từsrc1src2, ở đâusrc1[]destbằng nhau và src2tách rời nhau, ở đâu src2[]dest[]bằng nhau và src1tách rời nhau, và ở đó cả ba mảng đều bằng nhau. Ngược lại, FORTRAN có thể xử lý cả bốn trường hợp mà không gặp khó khăn khi sử dụng cùng một mã nguồn và cùng một mã máy.

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.