Khi nào tôi nên sử dụng các mẫu biểu thức C ++ trong khoa học tính toán và khi nào tôi nên * không * sử dụng chúng?


24

Giả sử rằng tôi đang làm việc với một mã khoa học trong C ++. Trong một cuộc thảo luận gần đây với một đồng nghiệp, người ta đã lập luận rằng các mẫu biểu thức có thể là một điều thực sự tồi tệ, có khả năng làm cho phần mềm chỉ có thể biên dịch được trên các phiên bản gcc nhất định. Giả sử, vấn đề này đã ảnh hưởng đến một vài mã khoa học, như được đề cập trong phụ đề của trò nhại lại này . (Đây là những ví dụ duy nhất tôi biết, do đó liên kết.)

Tuy nhiên, những người khác đã lập luận rằng các mẫu biểu thức là hữu ích vì chúng có thể mang lại hiệu suất tăng, như trong bài viết này trong Tạp chí tính toán khoa học SIAM , bằng cách tránh lưu trữ kết quả trung gian trong các biến tạm thời.

Tôi không biết nhiều về siêu lập trình mẫu trong C ++, nhưng tôi biết rằng đó là một cách tiếp cận được sử dụng trong phân biệt tự động và trong số học khoảng, đó là cách tôi tham gia thảo luận về các mẫu biểu thức. Với cả những lợi thế tiềm năng trong hiệu suất và những nhược điểm tiềm tàng trong bảo trì (nếu đó là từ đúng), khi nào tôi nên sử dụng các mẫu biểu thức C ++ trong khoa học tính toán và khi nào tôi nên tránh chúng?


Ah, video quá buồn cười. Tôi không biết nó tồn tại. Ai làm ra nó, bạn có biết không?
Wolfgang Bangerth

Không ý kiến; một vài người trong số các PETSc đã gửi cho tôi các liên kết tại một thời điểm. Tôi nghĩ rằng một nhà phát triển FEniCS đã làm nó.
Geoff Oxberry

Liên kết video bị hỏng và tôi sắp chết vì tò mò. Liên kết mới?
Praxeolitic

Oh drat, nevermind, tôi thấy youtube đã đến với các video Hitler của chúng tôi.
Praxeolitic

Câu trả lời:


17

Vấn đề của tôi với các mẫu biểu thức là chúng là một sự trừu tượng rất rò rỉ. Bạn dành rất nhiều công việc để viết mã rất phức tạp để thực hiện một nhiệm vụ đơn giản với cú pháp đẹp hơn. Nhưng nếu bạn muốn thay đổi thuật toán, bạn phải làm rối với mã bẩn và nếu bạn trượt theo các loại hoặc cú pháp, bạn sẽ nhận được các thông báo lỗi hoàn toàn không thể hiểu được. Nếu ứng dụng của bạn ánh xạ hoàn hảo đến một thư viện dựa trên các mẫu biểu thức, thì nó có thể đáng xem xét, nhưng nếu bạn không chắc chắn, tôi khuyên bạn chỉ nên viết mã bình thường. Chắc chắn, mã cấp cao ít đẹp hơn, nhưng bạn chỉ có thể làm những gì cần phải làm. Như một lợi ích, thời gian biên dịch và kích thước nhị phân sẽ giảm xuống và bạn sẽ không phải đối phó với sự khác biệt lớn về hiệu suất do sự lựa chọn cờ biên dịch và biên dịch.


Vâng, tôi đã thấy một số thông báo lỗi dài khi tôi phải chuyển mã từ gcc 2.95 sang gcc 4.x và trình biên dịch đã đưa ra tất cả các loại lỗi về mẫu. Một người bạn trong phòng thí nghiệm của tôi đang phát triển một thư viện templated để tính toán số học trong C ++ (thêm các tính năng mới không có trong Boost :: Interval để thực hiện nhiều nghiên cứu hơn) và tôi không muốn thấy mã trở thành cơn ác mộng để biên dịch.
Geoff Oxberry

12

Những người khác đã bình luận về vấn đề khó khăn như thế nào khi viết các chương trình ET cũng như sự phức tạp của việc hiểu các thông báo lỗi. Hãy để tôi nhận xét về vấn đề của trình biên dịch: Đúng là một trong những vấn đề lớn là tìm một trình biên dịch đủ tuân thủ tiêu chuẩn C ++ để làm cho mọi thứ hoạt động và làm cho nó hoạt động tốt. Kết quả là, chúng tôi đã tìm thấy rất nhiều lỗi - Tôi có 2-300 báo cáo lỗi trong tên của mình, được phân phối trên gcc, Intel icc, IBM xlC và pgicc của Portland. Do đó, tập lệnh cấu hình deal.II là một kho chứa một số lượng lớn các kiểm tra lỗi trình biên dịch, chủ yếu trong lĩnh vực mẫu, khai báo bạn bè, không gian tên, v.v.

Nhưng, hóa ra các nhà sản xuất trình biên dịch đã thực sự có được hành động của họ với nhau: hôm nay, gcc và icc hôm nay vượt qua tất cả các thử nghiệm của chúng tôi và thật dễ dàng để viết mã di động giữa hai người họ. Tôi có thể nói PGI không bị bỏ lại phía sau nhưng nó có một số điểm kỳ quặc dường như không biến mất trong những năm qua. Mặt khác, xlC là một câu chuyện hoàn toàn khác - họ sửa lỗi 6 tháng một lần, nhưng mặc dù đã báo cáo lỗi với họ trong nhiều năm, tiến độ rất chậm và xlC chưa bao giờ có thể biên dịch thỏa thuận.II thành công.

Tất cả điều này có nghĩa là gì: nếu bạn gắn bó với hai trình biên dịch lớn, bạn có thể mong đợi rằng chúng chỉ hoạt động ngày hôm nay. Vì hầu hết các máy tính và HĐH ngày nay thường có ít nhất một trong số chúng, thế là đủ. Nền tảng duy nhất mà mọi thứ khó khăn hơn là BlueGene, nơi trình biên dịch hệ thống thường là xlC, với tất cả các lỗi của nó.


Vì tò mò, bạn đã thử biên dịch với trình biên dịch xlc mới trên / Q chưa?
Aron Ahmadia

Không. Tôi sẽ thừa nhận rằng tôi đã từ bỏ xlC.
Wolfgang Bangerth

5

Tôi đã thử nghiệm một chút với ET từ lâu khi mà như bạn đã đề cập, các trình biên dịch vẫn đang vật lộn với chúng. Tôi đã sử dụng thư viện blitz cho đại số tuyến tính trong một số mã của tôi. Vấn đề sau đó là có được trình biên dịch tốt và vì tôi không phải là một lập trình viên C ++ hoàn hảo, diễn giải các thông báo lỗi trình biên dịch. Thứ hai chỉ đơn giản là không thể quản lý. Trình biên dịch, trung bình, sẽ tạo ra khoảng 1000 dòng thông báo lỗi. Không có cách nào tôi có thể nhanh chóng tìm thấy lỗi lập trình của tôi.

Bạn có thể tìm thêm thông tin trên trang web oonumerics (có các thủ tục tố tụng của hai hội thảo ET).

Nhưng tôi sẽ tránh xa họ ....


Các thông báo lỗi trình biên dịch thực sự là một trong những mối quan tâm của tôi. Với một số mã C ++ được tạo mẫu mà tôi biên dịch để xây dựng thư viện cho các dự án của mình, trình biên dịch có thể tạo ra hàng trăm dòng thông báo cảnh báo. Tuy nhiên, đó không phải là mã của tôi, tôi không hiểu nó và nói chung, nó hoạt động, vì vậy tôi để nó một mình. Các thông báo lỗi khó hiểu, dài dòng không tốt cho việc gỡ lỗi.
Geoff Oxberry

4

Vấn đề đã bắt đầu với thuật ngữ 'mẫu biểu thức (ET)'. Tôi không biết nếu có một định nghĩa chính xác cho nó. Nhưng theo cách sử dụng phổ biến, bằng cách nào đó, cặp đôi 'cách bạn mã hóa các biểu thức đại số tuyến tính' và 'cách nó được tính toán'. Ví dụ:

Bạn mã hoạt động vector

v = 2*x + 3*y + 4*z;                    // (1)

Và nó được tính toán bằng một vòng lặp

for (int i=0; i<n; ++i)                 // (2)
    v(i) = 2*x(i) + 3*y(i) + 4*z(i);

Theo tôi đây là hai điều khác nhau và cần được tách rời: (1) là một giao diện và (2) có thể thực hiện. Tôi có nghĩa là đây là thực tế phổ biến trong lập trình. Chắc chắn (2) có thể là một triển khai mặc định tốt, nhưng nói chung tôi muốn có thể sử dụng một triển khai chuyên dụng, chuyên dụng. Ví dụ, tôi muốn một chức năng như

myGreatVecSum(alpha, x, beta, y, gamma, z, result);    // (3)

được gọi khi tôi đang mã hóa (1). Có thể (3) chỉ sử dụng nội bộ một vòng lặp như trong (2). Nhưng tùy thuộc vào kích thước vectơ, việc triển khai khác có thể hiệu quả hơn. Dù sao, một số chuyên gia về hiệu suất cao có thể thực hiện và điều chỉnh (3) càng nhiều càng tốt. Vì vậy, nếu (1) không thể được ánh xạ tới một cuộc gọi của (3) thì tôi nên tránh đường cú pháp của (1) và gọi trực tiếp (3) ngay lập tức.

Những gì tôi mô tả là không có gì mới. Ngược lại, đó là ý tưởng đằng sau BLAS / LPACK:

  • Tất cả các hoạt động quan trọng về hiệu năng trong LAPACK được thực hiện bằng cách gọi các hàm BLAS.
  • BLAS chỉ định nghĩa một giao diện cho các biểu thức đại số tuyến tính thường là cần thiết.
  • Đối với BLAS thực hiện tối ưu hóa khác nhau tồn tại.

Nếu phạm vi của BLAS là không đủ (ví dụ: nó không cung cấp hàm như (3)) thì người ta có thể mở rộng phạm vi của BLAS. Vì vậy, con khủng long từ thập niên 60 và 70 này nhận ra với công cụ thời đồ đá của mình một sự tách biệt rõ ràng và trực giao giữa giao diện và cách thực hiện. Thật là buồn cười khi (hầu hết) các thư viện C ++ số không đạt được mức chất lượng phần mềm này. Mặc dù ngôn ngữ lập trình tự nó tinh vi hơn rất nhiều. Vì vậy, không có gì ngạc nhiên khi BLAS / LAPACK vẫn còn sống và được phát triển tích cực.

Vì vậy, theo tôi, ET không phải là xấu xa. Nhưng làm thế nào chúng thường được sử dụng trong các thư viện C ++ số đã khiến chúng bị mang tiếng xấu trong giới tính toán khoa học.


Michael, tôi nghĩ rằng bạn đang thiếu một trong những điểm của mẫu biểu thức. Ví dụ mã của bạn (1) không thực sự ánh xạ tới bất kỳ cuộc gọi BLAS nào được tối ưu hóa. Trong thực tế, ngay cả khi một thói quen BLAS tồn tại, tổng phí của lệnh gọi hàm BLAS làm cho nó khá khủng khiếp đối với các vectơ và ma trận nhỏ. Các thư viện mẫu biểu thức tinh vi như Blaze và Eigen có thể sử dụng đánh giá biểu thức bị trì hoãn để tránh sử dụng tạm thời, nhưng tôi tin rằng hầu như không có ngôn ngữ cụ thể nào trong miền sẽ có thể đánh bại đại số tuyến tính cuộn bằng tay.
Aron Ahmadia

Không, tôi nghĩ rằng bạn đang thiếu điểm. Bạn phải phân biệt giữa (a) BLAS như một đặc điểm kỹ thuật của một số hoạt động đại số tuyến tính cần thiết thường xuyên (b) triển khai BLAS như ATLAS, GotoBLAS, v.v. BTW rằng cách nó hoạt động trong FLENS: Theo mặc định, một biểu thức như (1) sẽ được đánh giá bằng cách gọi axpy từ BLAS ba lần. Nhưng không sửa đổi (1) tôi cũng có thể đánh giá nó như trong (2). Vì vậy, những gì xảy ra một cách hợp lý là như sau: nếu một hoạt động như trong (1) là quan trọng thì tập hợp các hoạt động BLAS được chỉ định (a) có thể được mở rộng.
Michael Lehn

Vì vậy, điểm quan trọng là: Các ký hiệu như 'v = x + y + z' và cuối cùng nó được tính toán như thế nào. Eigen, MTL, BLITZ, blaze-lib hoàn toàn thất bại về mặt này.
Michael Lehn

1
Đúng, nhưng số lượng các phép toán đại số tuyến tính thường xuyên cần thiết là tổ hợp. Nếu bạn định sử dụng một ngôn ngữ như C ++, bạn có thể lựa chọn sử dụng các mẫu biểu thức khi cần (đây là cách tiếp cận Eigen / Blaze) bằng cách kết hợp các khối con và thuật toán một cách thông minh bằng cách sử dụng đánh giá hoãn lại hoặc thực hiện một khối lớn thư viện của mọi thói quen có thể. Tôi không ủng hộ một trong hai cách tiếp cận, vì công việc gần đây ở Numba và Cython cho thấy chúng ta có thể có hiệu suất tương tự hoặc tốt hơn khi làm việc từ các ngôn ngữ kịch bản cấp cao như Python.
Aron Ahmadia

Nhưng một lần nữa, điều tôi phàn nàn là thực tế là các thư viện tinh vi (theo nghĩa phức tạp nhưng không linh hoạt) như Eigen kết hợp chặt chẽ giữa ký hiệu và cơ chế đánh giá và thậm chí nghĩ rằng đó là một điều tốt. Nếu tôi sử dụng một công cụ như Matlab, tôi chỉ muốn mã hóa mọi thứ và tin rằng Matlab đang làm điều tốt nhất có thể. Nếu tôi sử dụng một ngôn ngữ như C ++ thì tôi muốn được kiểm soát. Vì vậy, đánh giá cao nếu một cơ chế đánh giá mặc định tồn tại nhưng nó phải có thể thay đổi nó. Nếu không, tôi quay lại và gọi các chức năng trong C ++ trực tiếp.
Michael Lehn
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.