Làm cách nào để cải thiện hiệu suất thông qua cách tiếp cận cấp cao khi triển khai các phương trình dài trong C ++


92

Tôi đang phát triển một số mô phỏng kỹ thuật. Điều này liên quan đến việc thực hiện một số phương trình dài, chẳng hạn như phương trình này để tính toán ứng suất trong vật liệu như cao su:

T = (
    mu * (
            pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
            * (
                pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
                - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
            ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l1
            - pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l1 / 0.3e1
            - pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l1 / 0.3e1
        ) / a
    + K * (l1 * l2 * l3 - 0.1e1) * l2 * l3
) * N1 / l2 / l3

+ (
    mu * (
        - pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l2 / 0.3e1
        + pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
        * (
            pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
            - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
        ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l2
        - pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l2 / 0.3e1
    ) / a
    + K * (l1 * l2 * l3 - 0.1e1) * l1 * l3
) * N2 / l1 / l3

+ (
    mu * (
        - pow(l1 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l3 / 0.3e1
        - pow(l2 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a / l3 / 0.3e1
        + pow(l3 * pow(l1 * l2 * l3, -0.1e1 / 0.3e1), a) * a
        * (
            pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
            - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1
        ) * pow(l1 * l2 * l3, 0.1e1 / 0.3e1) / l3
    ) / a
+ K * (l1 * l2 * l3 - 0.1e1) * l1 * l2
) * N3 / l1 / l2;

Tôi sử dụng Maple để tạo mã C ++ để tránh sai lầm (và tiết kiệm thời gian với đại số tẻ nhạt). Vì mã này được thực thi hàng nghìn (nếu không phải hàng triệu) lần, nên hiệu suất là một mối quan tâm. Thật không may, toán học chỉ đơn giản hóa cho đến nay; các phương trình dài là không thể tránh khỏi.

Tôi có thể thực hiện cách tiếp cận nào để tối ưu hóa việc triển khai này? Tôi đang tìm kiếm các chiến lược cấp cao mà tôi nên áp dụng khi thực hiện các phương trình như vậy, không nhất thiết phải là các tối ưu hóa cụ thể cho ví dụ được hiển thị ở trên.

Tôi đang biên dịch bằng g ++ với --enable-optimize=-O3.

Cập nhật:

Tôi biết có rất nhiều biểu thức lặp lại, tôi đang sử dụng giả định rằng trình biên dịch sẽ xử lý chúng; các thử nghiệm của tôi cho đến nay cho thấy nó không.

l1, l2, l3, mu, a, K là tất cả các số thực dương (không phải số 0).

Tôi đã thay thế l1*l2*l3với một biến tương đương: J. Điều này đã giúp cải thiện hiệu suất.

Thay thế pow(x, 0.1e1/0.3e1)bằng cbrt(x)là một gợi ý hay.

Điều này sẽ được chạy trên CPU, Trong tương lai gần, điều này có thể sẽ chạy tốt hơn trên GPU, nhưng hiện tại tùy chọn đó không khả dụng.


32
Điều đầu tiên bạn nghĩ đến (trừ khi trình biên dịch sẽ tự tối ưu hóa nó) là thay thế tất cả những thứ đó pow(l1 * l2 * l3, -0.1e1 / 0.3e1)bằng một biến ... Tuy nhiên, bạn cần phải chuẩn mã của mình để chắc chắn rằng nó chạy nhanh hay chậm.
SingerOfTheFall

6
Đồng thời định dạng mã để làm cho mã dễ đọc hơn - có thể giúp xác định các khả năng cải tiến.
Ed Heal

26
Tại sao tất cả các phiếu phản đối và phiếu bầu đóng lại? Đối với những bạn không thích lập trình số hoặc khoa học, hãy xem các câu hỏi khác. Đây là một câu hỏi hay rất phù hợp với trang web này. Trang web scicomp vẫn là bản beta; di cư không phải là một lựa chọn tốt. Trang web đánh giá mã không có đủ con mắt tinh vi. Những gì OP đã làm xảy ra khá thường xuyên trong máy tính khoa học: Xây dựng một vấn đề trong một chương trình toán tượng trưng, ​​yêu cầu chương trình tạo mã và không chạm vào kết quả vì mã được tạo ra là một mớ hỗn độn.
David Hammen

6
@DavidHammen , trang web Đánh giá mã không có đủ con mắt tinh tường - nghe có vẻ giống như một vấn đề gà và trứng và một tư duy không giúp CR có thêm bất kỳ đôi mắt nào như vậy. Điều tương tự cũng áp dụng cho ý tưởng từ chối trang web beta của scicomp vì nó là bản beta - nếu mọi người nghĩ như vậy, trang web duy nhất phát triển sẽ là Stack Overflow.
Mathieu Guindon

13
Câu hỏi này đã được thảo luận trên meta đây
NathanOliver

Câu trả lời:


88

Chỉnh sửa tóm tắt

  • Câu trả lời ban đầu của tôi chỉ lưu ý rằng mã chứa rất nhiều phép tính được lặp lại và nhiều quyền hạn liên quan đến hệ số 1/3. Ví dụ, pow(x, 0.1e1/0.3e1)giống như cbrt(x).
  • Lần chỉnh sửa thứ hai của tôi chỉ là sai, và lần thứ ba của tôi ngoại suy cho sự sai lầm này. Đây là điều khiến mọi người sợ thay đổi kết quả giống như lời tiên tri từ các chương trình toán biểu tượng bắt đầu bằng chữ 'M'. Tôi đã bị ảnh hưởng ra (ví dụ, đình công ) những chỉnh sửa và đẩy nó vào phần cuối của phiên bản hiện tại của câu trả lời này. Tuy nhiên, tôi đã không xóa chúng. Tôi là người. Rất dễ để chúng tôi mắc sai lầm.
  • Chỉnh sửa thứ tư của tôi đã phát triển một biểu hiện rất nhỏ gọn mà chính xác đại diện cho sự biểu hiện phức tạp trong câu hỏi NẾU các thông số l1, l2l3là những con số thực dương và nếu alà một tổ chức phi zero số thực. (Chúng tôi vẫn chưa nhận được phản hồi từ OP về bản chất cụ thể của các hệ số này. Với bản chất của vấn đề, đây là những giả định hợp lý.)
  • Chỉnh sửa này cố gắng giải đáp vấn đề chung về cách đơn giản hóa các biểu thức này.

Điều đầu tiên trước tiên

Tôi sử dụng Maple để tạo mã C ++ để tránh những sai lầm.

Maple và Mathematica đôi khi bỏ lỡ điều hiển nhiên. Thậm chí quan trọng hơn, người dùng Maple và Mathematica đôi khi mắc lỗi. Thay thế "thường xuyên", hoặc thậm chí có thể "hầu như luôn luôn", thay cho "đôi khi có lẽ gần với dấu hơn.

Bạn có thể đã giúp Maple đơn giản hóa biểu thức đó bằng cách cho nó biết về các tham số được đề cập. Trong ví dụ ở tay, tôi nghi ngờ rằng l1, l2l3là những con số thực dương và đó alà một số thực khác không. Nếu đúng như vậy, hãy nói với nó điều đó. Các chương trình toán biểu tượng đó thường giả định các đại lượng trong tầm tay là phức tạp. Việc hạn chế miền cho phép chương trình đưa ra các giả định không hợp lệ trong các số phức.


Làm thế nào để đơn giản hóa những mớ hỗn độn lớn đó từ các chương trình toán biểu tượng (chỉnh sửa này)

Các chương trình toán tượng trưng thường cung cấp khả năng cung cấp thông tin về các tham số khác nhau. Sử dụng khả năng đó, đặc biệt nếu vấn đề của bạn liên quan đến phép chia hoặc lũy thừa. Trong ví dụ bên mình, bạn có thể đã giúp Maple đơn giản hóa biểu thức bằng cách nói với nó rằng l1, l2l3là những con số thực dương và đó alà một số thực khác không. Nếu đúng như vậy, hãy nói với nó điều đó. Các chương trình toán biểu tượng đó thường giả định các đại lượng trong tầm tay là phức tạp. Việc giới hạn miền cho phép chương trình đưa ra các giả định như a x b x = (ab) x . Đây chỉ là nếu ablà các số thực dương và nếu xlà thực. Nó không hợp lệ trong các số phức.

Cuối cùng, các chương trình toán biểu tượng đó tuân theo các thuật toán. Giúp nó cùng. Hãy thử chơi với việc mở rộng, thu thập và đơn giản hóa trước khi bạn tạo mã. Trong trường hợp này, bạn có thể đã thu thập những thuật ngữ liên quan đến hệ số muvà những thuật ngữ liên quan đến hệ số K. Giảm một biểu thức về "dạng đơn giản nhất" của nó vẫn là một nghệ thuật.

Khi bạn nhận được một mớ mã được tạo xấu xí, đừng chấp nhận nó như một sự thật mà bạn không được chạm vào. Cố gắng đơn giản hóa nó cho mình. Hãy xem chương trình toán biểu tượng có gì trước khi tạo mã. Hãy xem cách tôi giảm biểu hiện của bạn thành một thứ gì đó đơn giản và nhanh hơn nhiều, và câu trả lời của Walter đã đưa tôi tiến thêm vài bước. Không có công thức kỳ diệu. Nếu có một công thức ma thuật, Maple sẽ áp dụng nó và đưa ra câu trả lời mà Walter đưa ra.


Về câu hỏi cụ thể

Bạn đang thực hiện rất nhiều phép tính cộng và trừ trong phép tính đó. Bạn có thể gặp rắc rối lớn nếu bạn có các điều khoản gần như hủy bỏ nhau. Bạn đang lãng phí rất nhiều CPU nếu bạn có một thuật ngữ chiếm ưu thế hơn các thuật ngữ khác.

Tiếp theo, bạn đang lãng phí rất nhiều CPU do thực hiện các phép tính lặp đi lặp lại. Trừ khi bạn đã bật -ffast-math, điều này cho phép trình biên dịch phá vỡ một số quy tắc của dấu chấm động IEEE, trình biên dịch sẽ không (trên thực tế, không được) đơn giản hóa biểu thức đó cho bạn. Thay vào đó, nó sẽ làm chính xác những gì bạn đã yêu cầu. Ít nhất, bạn nên tính toán l1 * l2 * l3trước khi tính toán mớ hỗn độn đó.

Cuối cùng, bạn đang thực hiện rất nhiều cuộc gọi đến pow, cuộc gọi này cực kỳ chậm. Lưu ý rằng một số lệnh gọi đó có dạng (l1 * l2 * l3) (1/3) . Nhiều cuộc gọi đến powcó thể được thực hiện chỉ với một lệnh gọi tới std::cbrt:

l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;

Với cái này,

  • X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)trở thành X * l123_pow_1_3.
  • X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)trở thành X / l123_pow_1_3.
  • X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)trở thành X * l123_pow_4_3.
  • X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)trở thành X / l123_pow_4_3.


Maple đã bỏ lỡ điều hiển nhiên.
Ví dụ: có một cách dễ dàng hơn để viết

(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)

Giả sử rằng l1, l2l3là số thực chứ không phải số phức, và gốc khối lập phương thực (chứ không phải gốc phức nguyên tắc) phải được trích xuất, phần trên giảm xuống còn

2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))

hoặc là

2.0/(3.0 * l123_pow_1_3)

Sử dụng cbrt_l123thay vì l123_pow_1_3, biểu thức khó chịu trong câu hỏi giảm xuống

l123 = l1 * l2 * l3; 
cbrt_l123 = cbrt(l123);
T = 
  mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                 + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                 + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
 +K*(l123-1.0)*(N1+N2+N3);

Luôn kiểm tra kỹ, nhưng cũng luôn đơn giản hóa.


Dưới đây là một số bước của tôi để đạt được ở trên:

// Step 0: Trim all whitespace.
T=(mu*(pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1+pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l2-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1+pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l3)/a+K*(l1*l2*l3-0.1e1)*l1*l2)*N3/l1/l2;

// Step 1:
//   l1*l2*l3 -> l123
//   0.1e1 -> 1.0
//   0.4e1 -> 4.0
//   0.3e1 -> 3
l123 = l1 * l2 * l3;
T=(mu*(pow(l1*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l1-pow(l2*pow(l123,-1.0/3),a)*a/l1/3-pow(l3*pow(l123,-1.0/3),a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l2/3+pow(l2*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l2-pow(l3*pow(l123,-1.0/3),a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l3/3-pow(l2*pow(l123,-1.0/3),a)*a/l3/3+pow(l3*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 2:
//   pow(l123,1.0/3) -> cbrt_l123
//   l123*pow(l123,-4.0/3) -> pow(l123,-1.0/3)
//   (pow(l123,-1.0/3)-pow(l123,-1.0/3)/3) -> 2.0/(3.0*cbrt_l123)
//   *pow(l123,-1.0/3) -> /cbrt_l123
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T=(mu*(pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1-pow(l2/cbrt_l123,a)*a/l1/3-pow(l3/cbrt_l123,a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l2/3+pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2-pow(l3/cbrt_l123,a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l3/3-pow(l2/cbrt_l123,a)*a/l3/3+pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 3:
//   Whitespace is nice.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
       -pow(l2/cbrt_l123,a)*a/l1/3
       -pow(l3/cbrt_l123,a)*a/l1/3)/a
   +K*(l123-1.0)*l2*l3)*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)*a/l2/3
       +pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
       -pow(l3/cbrt_l123,a)*a/l2/3)/a
   +K*(l123-1.0)*l1*l3)*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)*a/l3/3
       -pow(l2/cbrt_l123,a)*a/l3/3
       +pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a
   +K*(l123-1.0)*l1*l2)*N3/l1/l2;

// Step 4:
//   Eliminate the 'a' in (term1*a + term2*a + term3*a)/a
//   Expand (mu_term + K_term)*something to mu_term*something + K_term*something
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
       -pow(l2/cbrt_l123,a)/l1/3
       -pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
 +K*(l123-1.0)*l2*l3*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l2/3
       +pow(l2/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
       -pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
 +K*(l123-1.0)*l1*l3*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l3/3
       -pow(l2/cbrt_l123,a)/l3/3
       +pow(l3/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l3))*N3/l1/l2
 +K*(l123-1.0)*l1*l2*N3/l1/l2;

// Step 5:
//   Rearrange
//   Reduce l2*l3*N1/l2/l3 to N1 (and similar)
//   Reduce 2.0/(3.0*cbrt_l123)*cbrt_l123/l1 to 2.0/3.0/l1 (and similar)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  (mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1
       -pow(l2/cbrt_l123,a)/l1/3
       -pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l2/3
       +pow(l2/cbrt_l123,a)*2.0/3.0/l2
       -pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
 +(mu*(-pow(l1/cbrt_l123,a)/l3/3
       -pow(l2/cbrt_l123,a)/l3/3
       +pow(l3/cbrt_l123,a)*2.0/3.0/l3))*N3/l1/l2
 +K*(l123-1.0)*N1
 +K*(l123-1.0)*N2
 +K*(l123-1.0)*N3;

// Step 6:
//   Factor out mu and K*(l123-1.0)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu*(  ( pow(l1/cbrt_l123,a)*2.0/3.0/l1
         -pow(l2/cbrt_l123,a)/l1/3
         -pow(l3/cbrt_l123,a)/l1/3)*N1/l2/l3
      + (-pow(l1/cbrt_l123,a)/l2/3
         +pow(l2/cbrt_l123,a)*2.0/3.0/l2
         -pow(l3/cbrt_l123,a)/l2/3)*N2/l1/l3
      + (-pow(l1/cbrt_l123,a)/l3/3
         -pow(l2/cbrt_l123,a)/l3/3
         +pow(l3/cbrt_l123,a)*2.0/3.0/l3)*N3/l1/l2)
 +K*(l123-1.0)*(N1+N2+N3);

// Step 7:
//   Expand
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1*N1/l2/l3
      -pow(l2/cbrt_l123,a)/l1/3*N1/l2/l3
      -pow(l3/cbrt_l123,a)/l1/3*N1/l2/l3
      -pow(l1/cbrt_l123,a)/l2/3*N2/l1/l3
      +pow(l2/cbrt_l123,a)*2.0/3.0/l2*N2/l1/l3
      -pow(l3/cbrt_l123,a)/l2/3*N2/l1/l3
      -pow(l1/cbrt_l123,a)/l3/3*N3/l1/l2
      -pow(l2/cbrt_l123,a)/l3/3*N3/l1/l2
      +pow(l3/cbrt_l123,a)*2.0/3.0/l3*N3/l1/l2)
 +K*(l123-1.0)*(N1+N2+N3);

// Step 8:
//   Simplify.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
  mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                 + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                 + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
 +K*(l123-1.0)*(N1+N2+N3);


Câu trả lời sai, cố ý giữ lại cho khiêm tốn

Lưu ý rằng điều này được đánh dấu. Nó sai.

Cập nhật

Maple đã bỏ lỡ điều hiển nhiên. Ví dụ: có một cách dễ dàng hơn để viết

(pow (l1 * l2 * l3, -0,1e1 / 0,3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0,4e1 / 0,3e1) / 0,3e1)

Giả sử rằng l1, l2l3là số thực chứ không phải là số phức, và gốc lập phương thực (chứ không phải gốc phức nguyên tắc) được trích xuất, thì giá trị trên giảm xuống còn 0. Phép tính số 0 này được lặp đi lặp lại nhiều lần.

Cập nhật thứ hai

Nếu tôi đã làm đúng phép toán (không có đảm bảo rằng tôi đã làm đúng phép toán), biểu thức khó chịu trong câu hỏi giảm xuống

l123 = l1 * l2 * l3; 
cbrt_l123_inv = 1.0 / cbrt(l123);
nasty_expression =
    K * (l123 - 1.0) * (N1 + N2 + N3) 
    - (  pow(l1 * cbrt_l123_inv, a) * (N2 + N3) 
       + pow(l2 * cbrt_l123_inv, a) * (N1 + N3) 
       + pow(l3 * cbrt_l123_inv, a) * (N1 + N2)) * mu / (3.0*l123);

Trên giả định rằng l1, l2l3là những con số thực dương.


2
Chà, loại bỏ CSE sẽ hoạt động, không phụ thuộc vào ngữ nghĩa thoải mái (và OP được chỉ ra trong các nhận xét là nó có). Tất nhiên, nếu nó quan trọng (đo lường), thì điều đó nên được kiểm tra (lắp ráp được tạo ra). Quan điểm của bạn về các điều khoản thống trị, đơn giản hóa công thức bị thiếu, các chức năng chuyên biệt tốt hơn và nguy cơ hủy bỏ là rất tốt.
Deduplicator

3
@Deduplicator - Không có dấu phẩy động. Trừ khi người ta cho phép tối ưu hóa toán học không an toàn (ví dụ: bằng cách chỉ định -ffast-mathbằng gcc hoặc clang), trình biên dịch không thể dựa vào pow(x,-1.0/3.0)bằng x*pow(x,-4.0/3.0). Cái sau có thể chảy dưới trong khi cái đầu tiên có thể không. Để tuân thủ tiêu chuẩn dấu phẩy động, trình biên dịch không được tối ưu hóa phép tính đó về không.
David Hammen

Chà, đó là những thứ khá nhiều tham vọng hơn bất cứ điều gì tôi muốn.
Deduplicator

1
@Deduplicator: Như tôi đã nhận xét về một câu trả lời khác : Bạn cần các lệnh gọi -fno-math-errnogiống hệt nhau từ g ++ đến CSE pow. (Trừ khi có thể nó có thể chứng minh pow rằng sẽ không cần phải thiết lập errno?)
Peter Cordes

1
@Lefti - Thực hiện rất nhiều câu trả lời của Walter. Đó là một thỏa thuận nhanh hơn. Có một vấn đề tiềm ẩn với tất cả các câu trả lời này, đó là hủy số. Giả sử của bạn N1, N2N3không phải là tiêu cực, một trong những 2*N_i-(N_j+N_k)ý chí là tiêu cực, một sẽ là tích cực, và ý chí kia sẽ ở đâu đó giữa. Điều này có thể dễ dàng dẫn đến các vấn đề hủy bỏ số.
David Hammen

32

Điều đầu tiên cần lưu ý là nó powthực sự đắt tiền, vì vậy bạn nên loại bỏ điều này càng nhiều càng tốt. Quét qua biểu thức, tôi thấy nhiều lần lặp lại pow(l1 * l2 * l3, -0.1e1 / 0.3e1)pow(l1 * l2 * l3, -0.4e1 / 0.3e1). Vì vậy, tôi hy vọng sẽ thu được nhiều lợi nhuận từ việc tính toán trước:

 const double c1 = pow(l1 * l2 * l3, -0.1e1 / 0.3e1);
const double c2 = boost::math::pow<4>(c1);

nơi tôi đang sử dụng chức năng boost pow .

Hơn nữa, bạn có một số khác powvới số mũ a. Nếu alà Số nguyên và được biết đến tại thời điểm trình biên dịch, bạn cũng có thể thay thế chúng bằng boost::math::pow<a>(...)để đạt được hiệu suất cao hơn. Tôi cũng sẽ đề nghị thay thế các thuật ngữ như a / l1 / 0.3e1bằng a / (l1 * 0.3e1)vì phép nhân nhanh hơn sau đó phép chia.

Cuối cùng, nếu bạn sử dụng g ++, bạn có thể sử dụng -ffast-mathcờ cho phép trình tối ưu hóa tích cực hơn trong việc chuyển đổi phương trình. Đọc về những gì lá cờ này thực sự làm , vì nó có tác dụng phụ.


5
Trong mã của chúng tôi, việc sử dụng -ffast-mathdẫn đến mã không ổn định hoặc đưa ra các câu trả lời sai. Chúng tôi gặp sự cố tương tự với các trình biên dịch của Intel và phải sử dụng -fp-model precisetùy chọn, nếu không mã sẽ bị lỗi hoặc đưa ra câu trả lời sai. Vì vậy, -ffast-mathcó thể tăng tốc độ, nhưng tôi khuyên bạn nên tiếp tục rất thận trọng với tùy chọn đó, ngoài các tác dụng phụ được liệt kê trong câu hỏi được liên kết của bạn.
tpg2114

2
@ tpg2114: Theo thử nghiệm của tôi, bạn chỉ cần-fno-math-errno g ++ để có thể di chuyển các cuộc gọi giống hệt nhau powra khỏi vòng lặp. Đó là phần ít "nguy hiểm" nhất của -ffast-math, đối với hầu hết các mã.
Peter Cordes

1
@PeterCordes Đó là những kết quả thú vị! Chúng tôi cũng gặp phải vấn đề với pow tốc độ cực kỳ chậm và cuối cùng đã sử dụng bản dlsymhack được đề cập trong các nhận xét để tăng hiệu suất đáng kể khi chúng tôi thực sự có thể làm với độ chính xác thấp hơn một chút.
tpg2114

GCC sẽ không hiểu rằng pow là một chức năng thuần túy? Đó có lẽ là kiến ​​thức tích hợp sẵn.
usr

6
@usr: Đó chỉ là vấn đề, tôi nghĩ vậy. powkhông một chức năng thuần túy, theo tiêu chuẩn, bởi vì nó là vụ phải thiết lập errnotrong một số trường hợp. Việc đặt cờ chẳng hạn -fno-math-errnokhiến nó không được đặt errno(do đó vi phạm tiêu chuẩn), nhưng sau đó nó là một chức năng thuần túy và có thể được tối ưu hóa như vậy.
Nate Eldredge

20

Woah, thật là một biểu cảm. Tạo biểu thức với Maple thực sự là một lựa chọn không tối ưu ở đây. Kết quả đơn giản là không thể đọc được.

  1. đã chọn nói tên biến (không phải l1, l2, l3, nhưng ví dụ: chiều cao, chiều rộng, chiều sâu, nếu đó là ý nghĩa của chúng). Sau đó, bạn sẽ dễ dàng hiểu mã của riêng mình hơn.
  2. tính toán các điều khoản phụ mà bạn sử dụng nhiều lần, trả trước và lưu trữ kết quả trong các biến có tên nói.
  3. Bạn đề cập, rằng biểu thức được đánh giá rất nhiều lần. Tôi đoán, chỉ có một số tham số thay đổi trong vòng lặp bên trong nhất. Tính toán tất cả các điều khoản con bất biến trước vòng lặp đó. Lặp lại vòng lặp bên trong thứ hai và cứ tiếp tục như vậy cho đến khi tất cả các bất biến nằm ngoài vòng lặp.

Về mặt lý thuyết, trình biên dịch có thể làm tất cả những điều đó cho bạn, nhưng đôi khi không thể - ví dụ: khi lồng vòng lặp trải rộng trên nhiều chức năng trong các đơn vị biên dịch khác nhau. Dù sao đi nữa, điều đó sẽ cung cấp cho bạn mã dễ đọc, dễ hiểu và dễ bảo trì hơn nhiều.


8
"trình biên dịch nên làm điều đó, nhưng đôi khi nó không", là chìa khóa ở đây. bên cạnh khả năng đọc, tất nhiên.
Javier

3
Nếu trình biên dịch không được yêu cầu làm điều gì đó, thì giả sử điều đó hầu như luôn sai.
edmz

4
Re Chọn tên biến nói - Rất nhiều lần quy tắc hay đó không áp dụng khi bạn làm toán. Khi nhìn vào mã được cho là thực hiện một thuật toán trong một tạp chí khoa học, tôi muốn thấy các ký hiệu trong mã chính xác là những ký hiệu được sử dụng trong tạp chí. Thông thường, điều đó có nghĩa là những tên cực kỳ ngắn, có thể kèm theo chỉ số phụ.
David Hammen

8
"Kết quả đơn giản là không thể đọc được" - tại sao đó là một vấn đề? Bạn sẽ không quan tâm rằng đầu ra ngôn ngữ cấp cao từ trình tạo lexer- hoặc trình phân tích cú pháp là "không thể đọc được" (bởi con người). Điều quan trọng ở đây là đầu vào cho trình tạo mã (Maple) có thể đọc được và kiểm tra được. Điều không nên làm là chỉnh sửa mã đã tạo bằng tay, nếu bạn muốn tự tin rằng nó không có lỗi.
alephzero

3
@DavidHammen: Chà, trong trường hợp đó, những cái có một chữ cái "tên nói". Ví dụ: khi thực hiện hình học trong hệ tọa độ Cartesian 2 chiều, xkhông phảiy là các biến đơn ký tự vô nghĩa, chúng là các từ toàn bộ với một định nghĩa chính xác và một ý nghĩa được hiểu rõ và rộng rãi.
Jörg W Mittag,

17

Câu trả lời của David Hammen là tốt, nhưng vẫn chưa tối ưu. Hãy tiếp tục với biểu cảm cuối cùng của anh ấy (tại thời điểm viết bài này)

auto l123 = l1 * l2 * l3;
auto cbrt_l123 = cbrt(l123);
T = mu/(3.0*l123)*(  pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
                   + pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
                   + pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
  + K*(l123-1.0)*(N1+N2+N3);

mà có thể được tối ưu hóa hơn nữa. Đặc biệt, chúng ta có thể tránh lệnh gọi đến cbrt()và một trong các lệnh gọi tới pow()nếu khai thác một số đặc điểm nhận dạng toán học. Hãy làm điều này một lần nữa từng bước.

// step 1 eliminate cbrt() by taking the exponent into pow()
auto l123 = l1 * l2 * l3;
auto athird = 0.33333333333333333 * a; // avoid division
T = mu/(3.0*l123)*(  (N1+N1-N2-N3)*pow(l1*l1/(l2*l3),athird)
                   + (N2+N2-N3-N1)*pow(l2*l2/(l1*l3),athird)
                   + (N3+N3-N1-N2)*pow(l3*l3/(l1*l2),athird))
  + K*(l123-1.0)*(N1+N2+N3);

Lưu ý rằng tôi cũng đã tối ưu hóa 2.0*N1thành N1+N1v.v. Tiếp theo, chúng ta chỉ có thể thực hiện với hai lệnh gọi tới pow().

// step 2  eliminate one call to pow
auto l123 = l1 * l2 * l3;
auto athird = 0.33333333333333333 * a;
auto pow_l1l2_athird = pow(l1/l2,athird);
auto pow_l1l3_athird = pow(l1/l3,athird);
auto pow_l2l3_athird = pow_l1l3_athird/pow_l1l2_athird;
T = mu/(3.0*l123)*(  (N1+N1-N2-N3)* pow_l1l2_athird*pow_l1l3_athird
                   + (N2+N2-N3-N1)* pow_l2l3_athird/pow_l1l2_athird
                   + (N3+N3-N1-N2)/(pow_l1l3_athird*pow_l2l3_athird))
  + K*(l123-1.0)*(N1+N2+N3);

Vì các cuộc gọi đến pow()là hoạt động tốn kém nhất ở đây, nên giảm chúng càng nhiều càng tốt (hoạt động tốn kém tiếp theo là cuộc gọi cbrt(), chúng tôi đã loại bỏ).

Nếu ngẫu nhiên alà số nguyên, các lệnh gọi đến powcó thể được tối ưu hóa cho các lệnh gọi tới cbrt(cộng với lũy thừa số nguyên) hoặc nếu athirdlà nửa số nguyên, chúng ta có thể sử dụng sqrt(cộng lũy ​​thừa số nguyên). Hơn nữa, nếu có cơ hội nào l1==l2hay l1==l3hay l2==l3một hoặc cả hai cuộc gọi đến powcó thể được loại bỏ. Vì vậy, nên coi đây là những trường hợp đặc biệt nếu những cơ hội như vậy thực sự tồn tại.


@gnat Tôi đánh giá cao sự chỉnh sửa của bạn (bản thân tôi đã nghĩ đến việc đó), nhưng sẽ thấy nó công bằng hơn, nếu câu trả lời của David cũng liên kết đến câu này. Tại sao bạn không chỉnh sửa câu trả lời của David theo cách tương tự?
Walter

1
Tôi chỉ chỉnh sửa vì tôi thấy bạn đề cập đến nó một cách rõ ràng; Tôi đã đọc lại câu trả lời của David và không thể tìm thấy tài liệu tham khảo cho câu trả lời của bạn ở đó. Tôi cố gắng tránh các chỉnh sửa không rõ ràng 100% rằng những thứ tôi thêm vào phù hợp với ý định của tác giả
gnat

1
@Walter - Câu trả lời của tôi bây giờ liên kết đến câu trả lời của bạn.
David Hammen

1
Đó chắc chắn không phải là tôi. Tôi đã ủng hộ câu trả lời của bạn vài ngày trước. Tôi cũng nhận được một phản hồi ngẫu nhiên về câu trả lời của mình. Điều đôi khi xảy ra.
David Hammen

1
Bạn và tôi đã nhận được một phản đối nhỏ mỗi người. Nhìn vào tất cả các phiếu phản đối cho câu hỏi! Tính đến thời điểm hiện tại, câu hỏi đã nhận được 16 lượt phản đối. Nó cũng đã nhận được 80 phiếu ủng hộ, nhiều hơn bù lại tất cả những người phản đối đó.
David Hammen

12
  1. How many là "nhiều nhiều"?
  2. Mât bao lâu?
  3. Làm TẤT CẢ các thông số thay đổi giữa các tính toán lại các công thức này? Hoặc bạn có thể lưu vào bộ nhớ cache một số giá trị được tính toán trước?
  4. Tôi đã cố gắng đơn giản hóa thủ công công thức đó, muốn biết liệu nó có tiết kiệm được gì không?

    C1 = -0.1e1 / 0.3e1;
    C2 =  0.1e1 / 0.3e1;
    C3 = -0.4e1 / 0.3e1;
    
    X0 = l1 * l2 * l3;
    X1 = pow(X0, C1);
    X2 = pow(X0, C2);
    X3 = pow(X0, C3);
    X4 = pow(l1 * X1, a);
    X5 = pow(l2 * X1, a);
    X6 = pow(l3 * X1, a);
    X7 = a / 0.3e1;
    X8 = X3 / 0.3e1;
    X9 = mu / a;
    XA = X0 - 0.1e1;
    XB = K * XA;
    XC = X1 - X0 * X8;
    XD = a * XC * X2;
    
    XE = X4 * X7;
    XF = X5 * X7;
    XG = X6 * X7;
    
    T = (X9 * ( X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / l2 / l3 
      + (X9 * (-XE + X5 * XD - XG) / l2 + XB * l1 * l3) * N2 / l1 / l3 
      + (X9 * (-XE - XF + X6 * XD) / l3 + XB * l1 * l2) * N3 / l1 / l2;

[ADDED] Tôi đã làm việc nhiều hơn trên công thức ba dòng cuối cùng và nó đạt được vẻ đẹp này:

T = X9 / X0 * (
      (X4 * XD - XF - XG) * N1 + 
      (X5 * XD - XE - XG) * N2 + 
      (X5 * XD - XE - XF) * N3)
  + XB * (N1 + N2 + N3)

Hãy để tôi trình bày công việc của mình, từng bước:

T = (X9 * (X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / l2 / l3 
  + (X9 * (X5 * XD - XE - XG) / l2 + XB * l1 * l3) * N2 / l1 / l3 
  + (X9 * (X5 * XD - XE - XF) / l3 + XB * l1 * l2) * N3 / l1 / l2;


T = (X9 * (X4 * XD - XF - XG) / l1 + XB * l2 * l3) * N1 / (l2 * l3) 
  + (X9 * (X5 * XD - XE - XG) / l2 + XB * l1 * l3) * N2 / (l1 * l3) 
  + (X9 * (X5 * XD - XE - XF) / l3 + XB * l1 * l2) * N3 / (l1 * l2);

T = (X9 * (X4 * XD - XF - XG) + XB * l1 * l2 * l3) * N1 / (l1 * l2 * l3) 
  + (X9 * (X5 * XD - XE - XG) + XB * l1 * l2 * l3) * N2 / (l1 * l2 * l3) 
  + (X9 * (X5 * XD - XE - XF) + XB * l1 * l2 * l3) * N3 / (l1 * l2 * l3);

T = (X9 * (X4 * XD - XF - XG) + XB * X0) * N1 / X0 
  + (X9 * (X5 * XD - XE - XG) + XB * X0) * N2 / X0 
  + (X9 * (X5 * XD - XE - XF) + XB * X0) * N3 / X0;

T = X9 * (X4 * XD - XF - XG) * N1 / X0 + XB * N1 
  + X9 * (X5 * XD - XE - XG) * N2 / X0 + XB * N2
  + X9 * (X5 * XD - XE - XF) * N3 / X0 + XB * N3;


T = X9 * (X4 * XD - XF - XG) * N1 / X0 
  + X9 * (X5 * XD - XE - XG) * N2 / X0
  + X9 * (X5 * XD - XE - XF) * N3 / X0
  + XB * (N1 + N2 + N3)

2
Điều đó đáng chú ý, huh? :) FORTRAN, IIRC, được thiết kế để tính toán công thức hiệu quả ("FOR" là công thức).
Vlad Feinstein

Hầu hết các mã F77 mà tôi đã thấy trông giống như vậy (ví dụ: BLAS & NR). Rất vui mừng vì Fortran 90-> 2008 tồn tại :)
Kyle Kanos

Đúng. Nếu bạn đang dịch một công thức, cách nào tốt hơn là FORmulaTRANslation?
Brian Drummond

1
'Tối ưu hóa' của bạn tấn công sai chỗ. Các bit tốn kém là các cuộc gọi đến std::pow(), trong đó bạn vẫn có gấp 6, 3 lần so với mức cần thiết. Nói cách khác, mã của bạn chậm hơn 3 lần so với mức có thể.
Walter

7

Điều này có thể hơi ngắn gọn, nhưng tôi thực sự đã tìm thấy tốc độ tăng tốc tốt cho đa thức (nội suy các hàm năng lượng) bằng cách sử dụng Biểu mẫu Horner, về cơ bản viết lại ax^3 + bx^2 + cx + dthành d + x(c + x(b + x(a))). Điều này sẽ tránh được nhiều cuộc gọi lặp lại pow()và ngăn bạn làm những việc ngớ ngẩn như gọi riêng pow(x,6)pow(x,7)thay vì chỉ làmx*pow(x,6) .

Điều này không thể áp dụng trực tiếp cho vấn đề hiện tại của bạn, nhưng nếu bạn có đa thức bậc cao với lũy thừa số nguyên thì nó có thể hữu ích. Bạn có thể phải chú ý đến sự ổn định số và các vấn đề tràn vì thứ tự của các hoạt động rất quan trọng đối với điều đó (mặc dù nói chung, tôi thực sự nghĩ Horner Form giúp ích cho việc này, vì x^20xthường là nhiều thứ tự độ lớn khác nhau).

Cũng là một mẹo thực tế, nếu bạn chưa làm như vậy, hãy cố gắng đơn giản hóa biểu thức trong maple trước. Bạn có thể có được nó để thực hiện hầu hết các loại bỏ biểu thức con phổ biến cho bạn. Tôi không biết cụ thể nó ảnh hưởng đến trình tạo mã trong chương trình đó như thế nào, nhưng tôi biết trong Mathematica thực hiện FullSimplify trước khi tạo mã có thể dẫn đến sự khác biệt rất lớn.


Biểu mẫu Horner khá chuẩn để mã hóa đa thức và điều này không liên quan gì đến câu hỏi cả.
Walter

1
Điều này có thể đúng với ví dụ của anh ấy, nhưng bạn sẽ nhận thấy anh ấy nói "các phương trình thuộc loại này." Tôi nghĩ câu trả lời sẽ hữu ích nếu người đăng có bất kỳ đa thức nào trong hệ thống của mình. Tôi đặc biệt nhận thấy rằng các trình tạo mã cho các chương trình CAS như Mathematica và Maple có xu hướng KHÔNG cung cấp cho bạn biểu mẫu Horner trừ khi bạn yêu cầu cụ thể; chúng mặc định theo cách bạn thường viết đa thức như một con người.
neocpp

3

Có vẻ như bạn có rất nhiều thao tác lặp đi lặp lại.

pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
pow(l1 * l2 * l3, -0.4e1 / 0.3e1)

Bạn có thể tính toán trước những điều đó để không phải gọi powhàm liên tục có thể tốn kém.

Bạn cũng có thể tính toán trước

l1 * l2 * l3

khi bạn sử dụng thuật ngữ đó nhiều lần.


6
Tôi cá là trình tối ưu hóa đã làm điều này cho bạn ... mặc dù ít nhất nó làm cho mã dễ đọc hơn.
Karoly Horvath

Tôi đã làm điều này, nhưng nó không tăng tốc độ mọi thứ. Tôi nhận ra điều này là do việc tối ưu hóa trình biên dịch đã xử lý nó.

lưu trữ l1 * l2 * l3 làm những việc tốc độ lên, mặc dù không chắc chắn lý do tại sao với trình biên dịch tối ưu hóa

bởi vì trình biên dịch đôi khi không thể thực hiện một số tối ưu hóa hoặc thấy chúng xung đột với các tùy chọn khác.
Javier

1
Trên thực tế, trình biên dịch không được thực hiện những tối ưu hóa đó trừ khi -ffast-mathđược bật và như đã lưu ý trong nhận xét của @ tpg2114, việc tối ưu hóa đó có thể tạo ra kết quả cực kỳ không ổn định.
David Hammen

0

Nếu bạn có một cạc đồ họa Nvidia CUDA, bạn có thể cân nhắc việc giảm tải các phép tính xuống cạc đồ họa - bản thân nó phù hợp hơn cho các phép tính phức tạp về mặt tính toán.

https://developer.nvidia.com/how-to-cuda-c-cpp

Nếu không, bạn có thể muốn xem xét nhiều luồng để tính toán.


10
Câu trả lời này là trực giao với câu hỏi hiện tại. Mặc dù GPU có rất nhiều bộ xử lý, nhưng chúng khá chậm so với FPU được nhúng với CPU. Thực hiện một phép tính nối tiếp duy nhất với GPU là một tổn thất lớn. CPU phải lấp đầy đường dẫn đến GPU, đợi GPU chậm thực hiện tác vụ đơn lẻ đó rồi tải kết quả. Mặc dù GPU hoàn toàn tuyệt vời khi vấn đề đang xảy ra là có thể song song hóa hàng loạt, nhưng chúng thực sự tồi tệ khi thực hiện các tác vụ nối tiếp.
David Hammen

1
Trong câu hỏi ban đầu: "Vì mã này được thực thi nhiều lần, hiệu suất là một mối quan tâm.". Đó là một nhiều hơn "nhiều". Op có thể gửi các tính toán theo cách thức phân luồng.
user3791372

0

Bằng mọi cách, bạn có thể cung cấp phép tính một cách tượng trưng. Nếu có các phép toán vectơ, bạn có thể thực sự muốn điều tra bằng cách sử dụng blas hoặc lapack mà trong một số trường hợp có thể chạy các hoạt động song song.

Có thể hình dung được (có nguy cơ lạc đề?) Rằng bạn có thể sử dụng python với numpy và / hoặc scipy. Trong phạm vi có thể, các phép tính của bạn có thể dễ đọc hơn.


0

Như bạn đã hỏi rõ ràng về tối ưu hóa cấp cao, có thể đáng để thử các trình biên dịch C ++ khác nhau. Ngày nay, các trình biên dịch là những con thú tối ưu hóa rất phức tạp và các nhà cung cấp CPU có thể thực hiện các tối ưu hóa rất mạnh mẽ và cụ thể. Nhưng xin lưu ý, một số trong số chúng không miễn phí (nhưng có thể có một chương trình học miễn phí).

  • Bộ sưu tập trình biên dịch GNU miễn phí, linh hoạt và có sẵn trên nhiều kiến ​​trúc
  • Các trình biên dịch của Intel rất nhanh, rất đắt và cũng có thể tạo ra kết quả tốt cho các kiến ​​trúc AMD (tôi tin rằng có một chương trình học thuật)
  • Các trình biên dịch của Clang nhanh, miễn phí và có thể tạo ra kết quả tương tự như GCC (một số người nói rằng chúng nhanh hơn, tốt hơn, nhưng điều này có thể khác nhau đối với từng trường hợp ứng dụng, tôi khuyên bạn nên tự trải nghiệm)
  • PGI (Portland Group) không miễn phí như các trình biên dịch của Intel.
  • Trình biên dịch PathScale có thể mang lại kết quả tốt trên các kiến ​​trúc AMD

Tôi đã thấy các đoạn mã khác nhau về tốc độ thực thi theo hệ số 2, chỉ bằng cách thay đổi trình biên dịch (tất nhiên là có tối ưu hóa đầy đủ). Nhưng lưu ý kiểm tra danh tính của đầu ra. Để tối ưu hóa tích cực có thể dẫn đến đầu ra khác nhau, đó là điều bạn chắc chắn muốn tránh.

Chúc may mắn!

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.