2013-05-19 27 views
8

Tôi chạy qua câu hỏi này trên scicomp liên quan đến tính toán tổng. Ở đó, bạn có thể thấy c++ và triển khai fortran tương tự. Điều thú vị là tôi thấy phiên bản fortran nhanh hơn khoảng 32%.Một trường hợp thử nghiệm đơn giản giữa clang ++/g ++/gfortran

Tôi nghĩ, tôi không chắc chắn về kết quả của họ và cố gắng tái tạo tình hình. Dưới đây là (rất nhẹ) mã khác nhau Tôi chạy:

C++

#include <iostream> 
#include <complex> 
#include <cmath> 
#include <iomanip> 

int main() 
{ 
    const double alpha = 1; 
    std::cout.precision(16); 

    std::complex<double> sum = 0; 
    const std::complex<double> a = std::complex<double>(1,1)/std::sqrt(2.); 
    for (unsigned int k=1; k<10000000; ++k) 
    { 
     sum += std::pow(a, k)*std::pow(k, -alpha); 

     if (k % 1000000 == 0) 
      std::cout << k << ' ' << sum << std::endl; 
    } 

    return 0; 
} 

fortran

implicit none 
integer, parameter :: dp = kind(0.d0) 
complex(dp), parameter :: i_ = (0, 1) 

real(dp) :: alpha = 1 
complex(dp) :: s = 0 
integer :: k 
do k = 1, 10000000 
    s = s + ((i_+1)/sqrt(2._dp))**k * k**(-alpha) 
    if (modulo(k, 1000000) == 0) print *, k, s 
end do 
end 

tôi biên dịch mã trên sử dụng gcc 4.6.3clang 3.0 trên một máy Ubuntu 12.04 LTS tất cả với cờ -O3. Dưới đây là timings của tôi:

time ./a.out 

gfortran

real 0m1.538s 
user 0m1.536s 
sys  0m0.000s 

g ++

real 0m2.225s 
user 0m2.228s 
sys  0m0.000s 

kêu vang

real 0m1.250s 
user 0m1.244s 
sys  0m0.004s 

Điều thú vị là tôi cũng có thể thấy mã số fortran nhanh hơn số c++ bằng khoảng 32% khi sử dụng gcc. Tuy nhiên, sử dụng clang, tôi có thể thấy rằng mã c++ thực sự chạy nhanh hơn khoảng 19%. Dưới đây là câu hỏi của tôi:

  1. Tại sao mã g ++ được tạo chậm hơn gfortran? Kể từ khi họ là từ cùng một gia đình trình biên dịch có nghĩa là điều này có nghĩa là (this) fortran mã đơn giản có thể được dịch sang một mã nhanh hơn? Đây có phải là trường hợp chung với fortran vs C++ không?
  2. Tại sao clang hoạt động tốt ở đây? Có một fortran front-end cho trình biên dịch llvm? Nếu có, mã sẽ được tạo ra bởi mã đó thậm chí còn nhanh hơn không?

UPDATE:

Sử dụng -ffast-math -O3 tùy chọn tạo ra các kết quả như sau:

gfortran

real 0m1.515s 
user 0m1.512s 
sys  0m0.000s 

g ++

real 0m1.478s 
user 0m1.476s 
sys  0m0.000s 

kêu vang

real 0m1.253s 
user 0m1.252s 
sys  0m0.000s 

Npw g++ phiên bản đang chạy càng nhanh gfortran và vẫn clang là nhanh hơn cả. Thêm -fcx-fortran-rules vào các tùy chọn ở trên không làm thay đổi đáng kể kết quả

+5

Vui lòng cung cấp các tùy chọn trình biên dịch được sử dụng để biên dịch. Các tùy chọn như -ffast-math cho gcc có thể ảnh hưởng đáng kể đến thời gian. –

+0

@NikolayViskov cờ duy nhất mà tôi sử dụng rõ ràng là '-O3' trên tất cả các trình biên dịch. – GradGuy

+0

Trên máy của tôi: clang '0.62' (-ffast-math' 0.60'), g ++ 4.6 '1.23' (-ffast-math' 0.78'), g ++ 4.7 '1.19' (-ffast-math' 0.76 ') – leemes

Trả lời

1

Tôi tin rằng sự cố của bạn nằm ở phần đầu ra. Người ta biết rằng các luồng C++ (std::cout) thường rất kém hiệu quả. Trong khi các trình biên dịch khác nhau có thể tối ưu hóa điều này, bạn nên viết lại các phần hiệu năng quan trọng bằng cách sử dụng hàm C printf thay vì std::cout.

+2

Tôi không nghĩ rằng 10 bản in sẽ ảnh hưởng đến hiệu suất trong phạm vi ~ 1.0 s. – steabert

+0

Bạn có thể đúng. Tôi không tự mình kiểm tra, nhưng đó chắc chắn là một suy nghĩ nên luôn luôn nhìn vào. – varepsilon

1

Sự khác biệt về thời gian sẽ liên quan đến thời gian cần thực hiện pow vì mã khác tương đối đơn giản. Bạn có thể kiểm tra điều này bằng cách lược tả. Câu hỏi sau đó là những gì trình biên dịch để tính toán chức năng điện?

Thời gian của tôi: ~ 1,20 giây cho phiên bản Fortran với gfortran -O3 và 1,07 giây cho phiên bản C++ được biên dịch với g++ -O3 -ffast-math. Lưu ý rằng -ffast-math không quan trọng đối với gfortran, vì pow sẽ được gọi từ thư viện nhưng điều này tạo ra sự khác biệt lớn cho g++.

Trong trường hợp của tôi, đối với gfortran, đó là chức năng _gfortran_pow_c8_i4 được gọi (source code). Việc triển khai của chúng là cách thông thường để tính toán các số nguyên nguyên. Mặt khác, với g++, đó là một mẫu hàm từ thư viện libstdC++, nhưng tôi không biết cách thực hiện. Rõ ràng, nó tốt hơn một chút bằng văn bản/tối ưu hóa. Tôi không biết chức năng này được biên dịch nhanh đến mức nào, coi đó là một mẫu. Đối với những gì nó có giá trị, phiên bản Fortran biên dịch với ifort và phiên bản C++ được biên dịch với icc (sử dụng cờ tối ưu hóa -fast) đều cung cấp cùng một thời gian, vì vậy tôi đoán chúng sử dụng cùng chức năng thư viện.

Nếu tôi chỉ viết chức năng nguồn ở Fortran với số học phức tạp (viết một cách rõ ràng các phần thực và tưởng tượng), nhanh như phiên bản C++ được biên dịch với g++ (nhưng sau đó -ffast-math làm chậm nó xuống, vì vậy tôi chỉ bị mắc kẹt -O3 với gfortran):

complex(8) function pow_c8_i4(a, k) 
implicit none 

integer, intent(in) :: k 
complex(8), intent(in) :: a 

real(8) :: Re_a, Im_a, Re_pow, Im_pow, tmp 
integer :: i 

Re_pow = 1.0_8 
Im_pow = 0.0_8 
Re_a = real(a) 
Im_a = aimag(a) 
i = k 

do while (i.ne.0) 
    if (iand(i,1).eq.1) then 
    tmp = Re_pow 
    Re_pow = Re_pow*Re_a-Im_pow*Im_a 
    Im_pow = tmp *Im_a+Im_pow*Re_a 
    end if 
    i = ishft(i,-1) 
    tmp = Re_a 
    Re_a = Re_a**2-Im_a**2 
    Im_a = 2*tmp*Im_a 
end do 
pow_c8_i4 = cmplx(Re_pow,Im_pow,8) 
end function 

Theo kinh nghiệm của tôi, sử dụng các phần thực và phần ảo rõ ràng trong việc triển khai Fortran là nhanh hơn, allthough nó rất thuận tiện tất nhiên để sử dụng các loại phức tạp.

Lưu ý cuối cùng: mặc dù đây chỉ là một ví dụ, cách gọi hàm điện mỗi lần lặp lại cực kỳ không hiệu quả.Thay vào đó, bạn nên tất nhiên chỉ nhân a của chính nó mỗi lần lặp.

Các vấn đề liên quan