Trình biên dịch Fortran thực sự tốt hơn bao nhiêu?


74

Câu hỏi này là phần mở rộng của hai cuộc thảo luận được đưa ra gần đây trong phần trả lời " C ++ vs Fortran cho HPC ". Và đó là một chút thách thức hơn là một câu hỏi ...

Một trong những lập luận thường được nghe nhất ủng hộ Fortran là các trình biên dịch chỉ tốt hơn. Vì hầu hết các trình biên dịch C / Fortran đều có chung một mặt sau, mã được tạo cho các chương trình tương đương về mặt ngữ nghĩa ở cả hai ngôn ngữ nên giống hệt nhau. Tuy nhiên, người ta có thể lập luận rằng C / Fortran dễ dàng hơn / ít hơn cho trình biên dịch để tối ưu hóa.

Vì vậy, tôi quyết định thử một thử nghiệm đơn giản: tôi đã nhận được một bản sao của daxpy.fdaxpy.c và biên dịch chúng với gfortran / gcc.

Bây giờ daxpy.c chỉ là bản dịch f2c của daxpy.f (mã được tạo tự động, xấu như heck), vì vậy tôi đã lấy mã đó và làm sạch nó một chút (gặp daxpy_c), về cơ bản có nghĩa là viết lại vòng lặp trong cùng như

for ( i = 0 ; i < n ; i++ )
    dy[i] += da * dx[i];

Cuối cùng, tôi đã viết lại nó (nhập daxpy_cvec) bằng cú pháp vectơ của gcc:

#define vector(elcount, type)  __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;

vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
    vy[i] += va * vx[i];
    vy[i+1] += va * vx[i+1];
    }
for ( i = n & ~3 ; i < n ; i++ )
    dy[i] += da * dx[i];

Lưu ý rằng tôi sử dụng các vectơ có độ dài 2 (tất cả SSE2 cho phép) và tôi xử lý hai vectơ cùng một lúc. Điều này là do trên nhiều kiến ​​trúc, chúng ta có thể có nhiều đơn vị nhân hơn chúng ta có các phần tử vectơ.

Tất cả các mã được biên dịch bằng gfortran / gcc phiên bản 4.5 với các cờ "-O3 -Wall -msse2 -march = local -ffast-math -fomit-frame-con trỏ -malign-double -fstrict-aliasing". Trên máy tính xách tay của tôi (CPU Intel Core i5, M560, 2.67GHz) tôi đã nhận được đầu ra sau:

pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.

Vì vậy, mã Fortran ban đầu mất hơn 8.1 giây, quá trình dịch tự động mất 10,5 giây, việc triển khai C ngây thơ thực hiện trong 7.9 và mã được mã hóa rõ ràng thực hiện trong 5.6, ít hơn một chút.

Đó là Fortran chậm hơn một chút so với triển khai C ngây thơ và chậm hơn 50% so với triển khai C được vector hóa.

Vì vậy, đây là câu hỏi: Tôi là một lập trình viên C bản địa và vì vậy tôi khá tự tin rằng tôi đã làm tốt mã đó, nhưng mã Fortran đã được chạm vào lần cuối vào năm 1993 và do đó có thể hơi lỗi thời. Vì tôi không cảm thấy thoải mái khi viết mã ở Fortran như những người khác ở đây, mọi người có thể làm việc tốt hơn, tức là cạnh tranh hơn so với bất kỳ hai phiên bản C nào không?

Ngoài ra, có ai có thể thử bài kiểm tra này với icc / ifort không? Cú pháp vectơ có thể sẽ không hoạt động, nhưng tôi sẽ tò mò muốn xem phiên bản C ngây thơ hoạt động ở đó như thế nào. Bất cứ ai cũng có xlc / xlf nằm xung quanh.

Tôi đã tải lên các nguồn và Makefile tại đây . Để có được thời gian chính xác, hãy đặt CPU_TPS trong test.c thành số Hz trên CPU của bạn. Nếu bạn tìm thấy bất kỳ cải tiến cho bất kỳ phiên bản nào, xin vui lòng gửi chúng ở đây!

Cập nhật:

Tôi đã thêm mã kiểm tra của stali vào các tệp trực tuyến và bổ sung mã đó bằng phiên bản C. Tôi đã sửa đổi các chương trình để thực hiện 1000'000 vòng trên các vectơ có độ dài 10.000 để phù hợp với thử nghiệm trước đó (và vì máy của tôi không thể phân bổ các vectơ có độ dài 1'000'000, như bản gốc của stali mã). Vì các số bây giờ nhỏ hơn một chút, tôi đã sử dụng tùy chọn -par-threshold:50để làm cho trình biên dịch có nhiều khả năng song song hơn. Phiên bản icc / ifort được sử dụng là 12.1.2 20111128 và kết quả như sau

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU

Tóm lại, kết quả là, cho tất cả các mục đích thực tế, giống hệt nhau cho cả phiên bản C và Fortran và cả hai mã đều song song tự động. Lưu ý rằng thời gian nhanh so với thử nghiệm trước đó là do sử dụng số học dấu phẩy động chính xác đơn!

Cập nhật:

Mặc dù tôi thực sự không thích gánh nặng chứng minh ở đâu, tôi đã mã hóa lại ví dụ nhân ma trận của stali trong C và thêm nó vào các tệp trên web . Dưới đây là kết quả của vòng lặp ba lần cho một và hai CPU:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 triple do time   3.46421700000000     
3.63user 0.06system 0:03.70elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 triple do time   5.09631900000000     
5.26user 0.06system 0:02.81elapsed 189%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU

Lưu ý rằng cpu_timetrong Fortran kiểm tra thời gian CPU chứ không phải thời gian đồng hồ treo tường, vì vậy tôi đã kết thúc các cuộc gọi timeđể so sánh chúng với 2 CPU. Không có sự khác biệt thực sự giữa các kết quả, ngoại trừ việc phiên bản C làm tốt hơn một chút trên hai lõi.

Bây giờ đối với matmullệnh, tất nhiên chỉ có ở Fortran vì nội tại này không có sẵn trong C:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 matmul    time   23.6494780000000     
23.80user 0.08system 0:23.91elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 matmul    time   26.6176640000000     
26.75user 0.10system 0:13.62elapsed 197%CPU

Ồ Điều đó hoàn toàn khủng khiếp. Bất cứ ai cũng có thể tìm ra những gì tôi đang làm sai, hoặc giải thích tại sao nội tại này vẫn là một điều tốt?

Tôi đã không thêm các dgemmcuộc gọi vào điểm chuẩn vì chúng là các cuộc gọi thư viện cho cùng chức năng trong Intel MKL.

Đối với các thử nghiệm trong tương lai, bất cứ ai cũng có thể đề xuất một ví dụ được biết là chậm hơn C trong Fortran?

Cập nhật

Để xác minh tuyên bố của stali rằng matmulnội tại là "một thứ tự phóng đại" nhanh hơn sản phẩm ma trận rõ ràng trên các ma trận nhỏ hơn, tôi đã sửa đổi mã của chính mình để nhân ma trận có kích thước 100x100 bằng cả hai phương pháp, mỗi lần 10.000 lần. Kết quả, trên một và hai CPU, như sau:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
 matmul    time   3.61222500000000     
 triple do time   3.54022200000000     
7.15user 0.00system 0:07.16elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
 matmul    time   4.54428400000000     
 triple do time   4.31626900000000     
8.86user 0.00system 0:04.60elapsed 192%CPU

Cập nhật

Grisu đúng khi chỉ ra rằng, không cần tối ưu hóa, gcc chuyển đổi các hoạt động trên các số phức thành các lệnh gọi hàm thư viện trong khi gfortran sắp xếp chúng theo một vài hướng dẫn.

Trình biên dịch C sẽ tạo cùng một mã nhỏ gọn nếu tùy chọn -fcx-limited-rangeđược đặt, tức là trình biên dịch được hướng dẫn bỏ qua các luồng trên / dưới tiềm năng trong các giá trị trung gian. Tùy chọn này bằng cách nào đó được đặt mặc định trong gfortran và có thể dẫn đến kết quả không chính xác. Buộc -fno-cx-limited-rangetrong gfortran không thay đổi bất cứ điều gì.

Vì vậy, đây thực sự là một đối số chống lại việc sử dụng gfortran cho các phép tính số: Các thao tác trên các giá trị phức tạp có thể vượt quá / chảy ngay cả khi kết quả chính xác nằm trong phạm vi dấu phẩy động. Đây thực sự là một tiêu chuẩn của Fortran. Trong gcc, hoặc trong C99 nói chung, mặc định là thực hiện mọi thứ một cách nghiêm ngặt (đọc tuân thủ theo chuẩn IEEE-754) trừ khi có quy định khác.

Nhắc nhở: Xin lưu ý rằng câu hỏi chính là liệu trình biên dịch Fortran có tạo ra mã tốt hơn trình biên dịch C hay không. Đây không phải là nơi để thảo luận về giá trị chung của ngôn ngữ này so với ngôn ngữ khác. Điều tôi sẽ thực sự quan tâm là nếu bất kỳ ai cũng có thể tìm ra cách dỗ gfortran để tạo ra một daxpy hiệu quả như trong C bằng cách sử dụng vector hóa rõ ràng vì điều này minh họa cho các vấn đề phải dựa vào trình biên dịch dành riêng cho tối ưu hóa SIMD, hoặc một trong trường hợp trình biên dịch Fortran thực hiện đối tác C của nó.


Một vấn đề thời gian là nếu bộ xử lý của bạn thực hiện chế độ bước / tần số, các kết quả này có thể xuất hiện trên toàn bản đồ.
Bill Barth

1
Daxpy_c.c của bạn hiện đang cập nhật x với bội số của x và hoàn toàn không chạm vào y. Bạn có thể muốn sửa nó để làm cho nó công bằng ...
Jack Poulson

1
@JackPoulson: Bắt tốt, sửa và cập nhật kết quả.
Pedro

2
Ngoài ra, tôi khá chắc chắn rằng sự khác biệt hoàn toàn là do việc hủy đăng ký thủ công trong phiên bản Fortran gây nhầm lẫn cho trình biên dịch. Khi tôi thay thế nó bằng cùng một vòng lặp đơn giản mà bạn đưa vào phiên bản C của mình, hiệu suất giữa hai phiên bản gần như giống hệt nhau. Không có thay đổi, phiên bản Fortran chậm hơn với trình biên dịch Intel.
Jack Poulson

1
@permeakra: Trên thực tế, tiêu chuẩn C99 chỉ định restricttừ khóa cho trình biên dịch biết chính xác rằng: giả sử rằng một mảng không trùng lặp với bất kỳ cấu trúc dữ liệu nào khác.
Pedro

Câu trả lời:


37

Sự khác biệt trong thời gian của bạn dường như là do việc hủy đăng ký thủ công của Fortran daxpy đơn vị . Các thời gian sau đây trên Xeon X5650 2,67 GHz, sử dụng lệnh

./test 1000000 10000

Trình biên dịch Intel 11.1

Fortran với chế độ hủy đăng ký thủ công: 8,7 giây
Fortran w / o unrolling thủ công: 5,8 giây
C w / o unrolling thủ công: 5,8 giây

Trình biên dịch GNU 4.1.2

Fortran với chế độ hủy đăng ký thủ công: 8,3 giây
Fortran w / o unrolling thủ công: 13,5 giây
C w / o unrolling thủ công: 13,6 giây
C với các thuộc tính vector: 5,8 giây

Trình biên dịch GNU 4.4.5

Fortran với chế độ hủy đăng ký thủ công: 8.1 giây
Fortran w / o unrolling thủ công: 7.4 giây
C w / o unrolling thủ công: 8,5 giây
C với vector atrote: 5,8 giây

Kết luận

  • Việc hủy đăng ký thủ công đã giúp trình biên dịch GNU 4.1.2 Fortran trên kiến ​​trúc này, nhưng làm tổn thương phiên bản mới hơn (4.4.5) và trình biên dịch Intel Fortran.
  • Trình biên dịch GNU 4.4.5 C cạnh tranh hơn nhiều với Fortran so với phiên bản 4.2.1.
  • Nội tại vector cho phép hiệu suất GCC phù hợp với trình biên dịch Intel.

Thời gian để kiểm tra các thói quen phức tạp hơn như dgemv và dgemm?


Cảm ơn về kết quả! Phiên bản gcc nào bạn đang sử dụng và bạn có thể nói cụ thể hơn một chút về CPU không?
Pedro

2
Trình biên dịch của bạn cũ hơn CPU của bạn ... Bạn có thể thử với gcc-4.5 không?
Pedro

1
Tôi chỉ thử nó. Phiên bản được vector hóa với GCC 4.4.5 hoàn toàn khớp với kết quả Intel 11.1.
Jack Poulson

1
Tôi vừa cài đặt gcc / gfortran phiên bản 4.4.5 và tôi không thể tái tạo sự khác biệt khi không đăng ký. Trong thực tế, trong trình biên dịch được tạo cho cả hai trường hợp, vòng lặp trong cùng là giống hệt nhau ngoại trừ các tên thanh ghi được sử dụng, có thể hoán đổi cho nhau. Bạn có thể chạy lại bài kiểm tra của bạn chỉ để chắc chắn?
Pedro

4
Chúng ta có thể nói kiểu giải quyết cuộc tranh luận lâu đời này "chúng ta tiếp tục sử dụng fortran vì nó hiệu quả hơn", để cuối cùng chúng ta có thể ném nó vào thùng rác?
Stefano Borini

16

Tôi đến muộn trong bữa tiệc này, vì vậy thật khó để tôi theo dõi qua lại từ tất cả những điều trên. Câu hỏi rất lớn, và tôi nghĩ rằng nếu bạn quan tâm thì nó có thể được chia thành nhiều phần nhỏ hơn. Một điều tôi quan tâm chỉ đơn giản là hiệu suất của các daxpybiến thể của bạn và liệu Fortran có chậm hơn C trên mã rất đơn giản này hay không.

Chạy cả trên máy tính xách tay của tôi (Macbook Pro, Intel Core i7, 2,66 GHz), hiệu suất tương đối của phiên bản C được vector hóa bằng tay và phiên bản Fortran không được vector hóa tùy thuộc vào trình biên dịch được sử dụng (với tùy chọn của riêng bạn):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

Vì vậy, có vẻ như GCC đã tốt hơn trong việc vector hóa vòng lặp trong nhánh 4.6 so với trước đây.


Về tranh luận tổng thể, tôi nghĩ người ta có thể viết mã nhanh và tối ưu hóa ở cả C và Fortran, gần giống như trong ngôn ngữ lắp ráp. Tuy nhiên, tôi sẽ chỉ ra một điều: giống như trình biên dịch chương trình viết sẽ tẻ nhạt hơn C nhưng cung cấp cho bạn quyền kiểm soát tốt hơn đối với những gì được CPU thực thi, C ở mức độ thấp hơn Fortran. Do đó, nó cung cấp cho bạn nhiều quyền kiểm soát hơn đối với các chi tiết, có thể giúp tối ưu hóa, trong đó cú pháp tiêu chuẩn Fortran (hoặc phần mở rộng nhà cung cấp của nó) có thể thiếu chức năng. Một trường hợp là việc sử dụng rõ ràng các loại vectơ, một trường hợp khác là khả năng chỉ định căn chỉnh các biến bằng tay, một cái gì đó Fortran không có khả năng.


Chào mừng đến với scicomp! Tôi đồng ý rằng các phiên bản trình biên dịch cũng quan trọng như ngôn ngữ trong trường hợp này. Ý của bạn là 'của' thay vì 'trong câu cuối cùng của bạn?
Aron Ahmadia

9

Cách tôi viết AXPY ở Fortran hơi khác một chút. Đây là bản dịch chính xác của toán học.

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

Bây giờ hãy gọi các thói quen trên trong một chương trình.

kiểm tra.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

Bây giờ hãy biên dịch và chạy nó ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

Lưu ý rằng tôi không sử dụng bất kỳ vòng lặp hoặc bất kỳ chỉ thị OpenMP rõ ràng nào . Điều này có thể xảy ra trong C (nghĩa là không sử dụng các vòng lặp và tự động song song)? Tôi không sử dụng C vì vậy tôi không biết.


Tự động song song hóa là một tính năng của trình biên dịch Intel (cả Fortran và C), chứ không phải ngôn ngữ. Do đó tương đương trong C cũng nên song song. Vì tò mò, làm thế nào để nó thực hiện cho n = 10000 vừa phải hơn?
Pedro

3
Đó là toàn bộ điểm. Autopar dễ dàng hơn trong Fortran do thực tế là Fortran (không giống như C) hỗ trợ toàn bộ các hoạt động mảng như matmult, chuyển đổi, v.v ... Vì vậy, tối ưu hóa mã dễ dàng hơn cho trình biên dịch Fortran. GFortran (mà bạn đã sử dụng) không có tài nguyên dành cho nhà phát triển để tối ưu hóa trình biên dịch Fortran vì trọng tâm của họ hiện là triển khai tiêu chuẩn Fortran 2003 thay vì tối ưu hóa.
stali

Uhmm ... Trình biên dịch Intel C / C ++ icccũng tự động song song hóa. Tôi đã thêm một tập tin icctest.cvào các nguồn khác. Bạn có thể biên dịch nó với các tùy chọn giống như bạn đã sử dụng ở trên, chạy nó và báo cáo thời gian không? Tôi đã phải thêm một câu lệnh printf vào mã của mình để tránh gcc tối ưu hóa mọi thứ. Đây chỉ là một bản hack nhanh và tôi hy vọng nó không có lỗi!
Pedro

Tôi đã tải xuống trình biên dịch icc / ifort mới nhất và tự mình thực hiện các bài kiểm tra. Câu hỏi đã được cập nhật để bao gồm các kết quả mới này, tức là việc tự động hóa của Intel hoạt động ở cả Fortran và C.
Pedro

1
Cảm ơn. Có, tôi nhận thấy rằng có rất ít sự khác biệt có lẽ bởi vì các vòng lặp rất đơn giản và các thao tác là BLAS cấp 1. Nhưng như tôi đã nói trước đây do khả năng của Fortran thực hiện toàn bộ hoạt động mảng và sử dụng các từ khóa như PURE / ElementAL, có nhiều chỗ hơn để tối ưu hóa trình biên dịch. Làm thế nào các trình biên dịch sử dụng thông tin này và những gì nó thực sự là một điều khác biệt. Bạn cũng có thể thử matmul nếu bạn muốn bpaste.net/show/23035
stali

6

Tôi nghĩ, nó không chỉ thú vị khi trình biên dịch tối ưu hóa mã cho phần cứng hiện đại. Đặc biệt là giữa GNU C và GNU Fortran, việc tạo mã có thể rất khác nhau.

Vì vậy, hãy xem xét một ví dụ khác để cho thấy sự khác biệt giữa chúng.

Sử dụng các số phức, trình biên dịch GNU C tạo ra một chi phí lớn cho hoạt động số học gần như rất cơ bản trên một số phức. Trình biên dịch Fortran cho mã tốt hơn nhiều. Chúng ta hãy xem ví dụ nhỏ sau đây ở Fortran:

COMPLEX*16 A,B,C
C=A*B

cho (gfortran -g -o Complex.fo -c Complex.f95; objdump -d -S Complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

Đó là mã máy 39 byte. Khi chúng ta xem xét tương tự trong C

 double complex a,b,c; 
 c=a*b; 

và hãy xem đầu ra (được thực hiện theo cách tương tự như trên), chúng tôi nhận được:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

Mã máy 39 byte cũng vậy, nhưng chức năng bước 57 đề cập đến, thực hiện phần công việc phù hợp và thực hiện thao tác mong muốn. Vì vậy, chúng tôi có mã máy 27 byte để chạy hoạt động đa. Hàm phía sau là muldc3 được cung cấp bởi libgcc_s.sovà có dấu chân là 1375 byte trong mã máy. Điều này làm chậm mã một cách đáng kể và cung cấp một đầu ra thú vị khi sử dụng một trình lược tả.

Khi chúng tôi triển khai các ví dụ BLAS ở trên cho zaxpyvà thực hiện cùng một bài kiểm tra, trình biên dịch Fortran sẽ cho kết quả tốt hơn trình biên dịch C.

(Tôi đã sử dụng GCC 4.4.3 cho thử nghiệm này, nhưng tôi nhận thấy hành vi này mà một GCC khác phát hành.)

Vì vậy, theo tôi, chúng ta không chỉ nghĩ về song song hóa và vector hóa khi chúng ta nghĩ về trình biên dịch nào tốt hơn mà chúng ta còn phải xem những thứ cơ bản được dịch sang mã trình biên dịch. Nếu bản dịch này cung cấp mã xấu, tối ưu hóa chỉ có thể sử dụng những thứ này làm đầu vào.


1
Tôi vừa đưa ra một ví dụ dọc theo dòng mã của bạn complex.cvà thêm nó vào mã trực tuyến. Tôi đã phải thêm tất cả đầu vào / đầu ra để đảm bảo không có gì được tối ưu hóa. Tôi chỉ nhận được một cuộc gọi __muldc3nếu tôi không sử dụng -ffast-math. Với -O2 -ffast-mathtôi nhận được 9 dòng lắp ráp nội tuyến. Bạn có thể xác nhận điều này?
Pedro

Tôi đã tìm thấy một nguyên nhân cụ thể hơn cho sự khác biệt trong trình biên dịch chương trình được tạo và đã thêm điều này vào câu hỏi của tôi ở trên.
Pedro

Sử dụng -O2 dẫn trình biên dịch để tính toán mọi thứ có thể trong thời gian chạy, đó là lý do tại sao các cấu trúc như vậy đôi khi bị mất. Tùy chọn -ffast-math không nên được sử dụng trong tính toán khoa học khi bạn muốn dựa vào kết quả đầu ra.
MK aka Grisu

1
Chà, theo lập luận đó (không -ffast-math), bạn không nên sử dụng Fortran cho các tính toán có giá trị phức tạp của mình. Như tôi mô tả trong bản cập nhật cho câu hỏi của tôi, -ffast-mathhay nói chung hơn -fcx-limited-range, buộc gcc phải sử dụng cùng các tính toán phạm vi bị hạn chế, không theo chuẩn như tiêu chuẩn trong Fortran. Vì vậy, nếu bạn muốn có đầy đủ các giá trị phức tạp và Infs và NaN chính xác, bạn không nên sử dụng Fortran ...
Pedro

2
@Pedro: Nếu bạn muốn GCC hành xử giống như wrort của GFortran. phép nhân và chia phức tạp, bạn nên sử dụng quy tắc -fcx-fortran.
janneb

4

Lớp người, dân chúng, dân gian,

Tôi thấy cuộc thảo luận này rất thú vị, nhưng tôi đã ngạc nhiên khi thấy rằng việc sắp xếp lại các vòng lặp trong ví dụ Matmul đã thay đổi hình ảnh. Tôi không có sẵn trình biên dịch intel trên máy hiện tại của mình, vì vậy tôi đang sử dụng gfortran, nhưng viết lại các vòng lặp trong mm_test.f90 để

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

đã thay đổi toàn bộ kết quả cho máy của tôi.

Các kết quả thời gian của phiên bản trước là:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

trong khi đó với các vòng lặp ba được sắp xếp lại như trên đã được ghi lại:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

Đây là gcc / gfortran 4.7.2 20121109 trên CPU Intel (R) Core (TM) i7-2600K @ 3.40GHz

Cờ trình biên dịch được sử dụng là những cờ từ Makefile tôi đã đến đây ...


3
Điều đó không có gì đáng ngạc nhiên, vì bộ lưu trữ ma trận trong bộ nhớ ưu tiên một thứ tự, nghĩa là, nếu các hàng được lưu trữ liên tục, tốt hơn là lặp qua các hàng trong cùng, từ đó bạn có thể tải từng hàng vào bộ nhớ cục bộ nhanh so với tải liên tục (một lát ) nó để truy cập một yếu tố duy nhất. Xem stackoverflow.com/questions/7395556 .
Christian Clason

Tôi đoán rằng tôi đã ngạc nhiên rằng "matmul nội tại" sẽ không được mã hóa để làm mọi thứ theo cách này. Nó thực sự nhanh hơn với bộ ba được đặt hàng theo cách thứ hai. Nó dường như nằm trong bộ trình biên dịch này, vì các phiên bản gfortran trước đây tôi có thể trở nên "phẳng" hơn trong thời gian của chúng - không quan trọng bằng cách nào bạn đã thực hiện nhiều lần - mất gần như cùng một lúc.
Schatzi

-2

Đó không phải là ngôn ngữ làm cho mã chạy nhanh hơn, mặc dù chúng có ích. Nó là trình biên dịch, CPU và hệ điều hành làm cho mã chạy nhanh hơn. So sánh các ngôn ngữ chỉ là một cách hiểu sai, vô dụng và vô nghĩa. Nó không có ý nghĩa gì vì bạn đang so sánh hai biến: ngôn ngữ và trình biên dịch. Nếu một mã chạy nhanh hơn, bạn không biết ngôn ngữ đó là bao nhiêu hoặc trình biên dịch đó là bao nhiêu. Tôi không hiểu tại sao cộng đồng khoa học máy tính không hiểu điều nà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.