Tối ưu hóa siêu C ++ của phép nhân ma trận với Armadillo


9

Tôi đang sử dụng Armadillo để thực hiện các phép nhân ma trận rất chuyên sâu với độ dài cạnh , trong đó n có thể lên tới 20 hoặc thậm chí hơn. Tôi đang sử dụng Armadillo với OpenBLAS để nhân ma trận, dường như đang làm rất tốt trong các lõi song song, ngoại trừ việc tôi gặp vấn đề với phép nhân trong Armadillo để tối ưu hóa hiệu suất.2nn

Nói rằng tôi có một vòng lặp ở dạng sau:

arma::cx_mat stateMatrix, evolutionMatrix; //armadillo complex matrix type
for(double t = t0; t < t1; t += 1/sampleRate)
{
    ...
    stateMatrix = evolutionMatrix*stateMatrix;
    ...
}

Trong C ++ cơ bản, tôi thấy vấn đề ở đây là C ++ sẽ phân bổ một đối tượng mới của cx_matcửa hàng evolutionMatrix*stateMatrix, và sau đó sao chép các đối tượng mới để stateMatrixoperator=(). Điều này rất, rất không hiệu quả. Mọi người đều biết rằng việc trả về các lớp dữ liệu lớn phức tạp là một ý tưởng tồi, phải không?

Cách tôi thấy cách này hiệu quả hơn là với một hàm thực hiện phép nhân dưới dạng:

void multiply(const cx_mat& mat1, const cx_mat& mat2, cx_mat& output)
{
    ... //multiplication of mat1 and mat2 and then store it in output
}

Theo cách này, Người ta không phải sao chép các đối tượng lớn có giá trị trả về và đầu ra không phải được phân bổ lại với mỗi phép nhân.

Câu hỏi : Làm thế nào tôi có thể tìm thấy một sự thỏa hiệp, trong đó tôi có thể sử dụng Armadillo để nhân với giao diện BLAS đẹp mắt của mình và thực hiện điều này một cách hiệu quả mà không phải tạo lại các đối tượng ma trận và sao chép chúng với mỗi thao tác?

Đây không phải là một vấn đề triển khai trong Armadillo sao?


4
"Siêu tối ưu hóa" thực sự là một điều mà có lẽ bạn không muốn nói đến. Đây là một hình thức chuyên môn mã thời gian biên dịch rất cũ và tiên tiến vẫn chưa được bắt kịp.
Andrew Wagner

1
Hầu hết các câu trả lời (và chính câu hỏi!) Dường như bỏ lỡ điểm rằng phép nhân ma trận không phải là thứ bạn làm tại chỗ.

@hurkyl bạn có ý nghĩa gì với "tại chỗ"?
Nhà vật lý lượng tử

Khi bạn tính , bạn sửa đổi A "tại chỗ" theo nghĩa là bạn để lại nội dung của A nơi chúng nằm trong bộ nhớ và thực hiện tất cả công việc sửa đổi bộ nhớ đó. Một = Một * B hoặc A = B * Một không được tính cách mà ở tất cả. Không có thuật toán hợp lý nào cho phép nhân rời khỏi vị trí của A trong bộ nhớ và ghi đầu ra của phép nhân vào cùng một bộ nhớ khi nó được tính toán. Bản cập nhật phải được thực hiện không đúng chỗ - bộ nhớ tạm thời phải được sử dụng trong một số thời trang. A=A+BAAA=ABA=BAA

Nhìn vào mã nguồn của Armadillo, biểu thức stateMatrix = evolutionMatrix*stateMatrixsẽ không sao chép bất cứ điều gì. Thay vào đó, Armadillo thay đổi bộ nhớ ưa thích. Bộ nhớ mới cho kết quả vẫn sẽ được phân bổ (không có cách nào khác), nhưng thay vì sao chép, stateMatrixma trận sẽ chỉ sử dụng bộ nhớ mới và loại bỏ bộ nhớ cũ.
mtall

Câu trả lời:


14

Trong C ++ cơ bản, tôi thấy vấn đề ở đây là C ++ sẽ phân bổ một đối tượng mới của cx_mat để lưu trữ EvolutionMatrix * stateMatrix, sau đó sao chép đối tượng mới sang stateMatrix với toán tử = ().

Tôi nghĩ rằng bạn đúng khi nó tạo ra những thứ tạm thời, quá chậm, nhưng tôi nghĩ lý do tại sao nó làm điều đó là sai.

Armadillo, giống như bất kỳ thư viện đại số tuyến tính C ++ tốt nào, sử dụng các mẫu biểu thức để thực hiện đánh giá biểu thức bị trì hoãn. Khi bạn viết ra một sản phẩm ma trận như A*B, không có temporaries được tạo ra, thay vì Armadillo làm cho một đối tượng tạm thời ( x) mà giữ tham chiếu đến AB, và sau đó, đưa ra một biểu hiện như C = x, tính tích ma trận lưu trữ kết quả trực tiếp trong C, mà không tạo ra bất kỳ tạm thời.

Nó cũng sử dụng tối ưu hóa này để xử lý các biểu thức như A*B*C*D, trong đó, tùy thuộc vào kích thước ma trận, các lệnh nhân nhất định sẽ hiệu quả hơn các biểu thức khác.

Đây không phải là một vấn đề triển khai trong Armadillo sao?

Nếu Armadillo không thực hiện tối ưu hóa này, đó sẽ là một lỗi trong Armadillo cần được báo cáo cho các nhà phát triển.

Tuy nhiên, trong trường hợp của bạn, có một vấn đề khác quan trọng hơn. Trong một biểu thức như A=B*Clưu trữ Akhông chứa bất kỳ dữ liệu đầu vào nào nếu Akhông bí danh Bhoặc C. Trong trường hợp của bạn A = A*B, việc viết bất cứ điều gì vào ma trận đầu ra cũng sẽ sửa đổi một trong các ma trận đầu vào.

Thậm chí cho chức năng được đề xuất của bạn

multiply(const cx_mat&, const cx_mat&, cx_mat&)

Làm thế nào chính xác chức năng đó sẽ giúp trong biểu thức multiply(A, B, A)? Đối với hầu hết các triển khai thông thường của chức năng đó, điều đó sẽ dẫn đến một lỗi. Nó sẽ cần phải sử dụng một số lưu trữ tạm thời, để đảm bảo dữ liệu đầu vào của nó không bị hỏng. Gợi ý của bạn là khá nhiều cách Armadillo thực hiện phép nhân ma trận, nhưng tôi nghĩ có lẽ nên cẩn thận để tránh các tình huống như multiply(A, B, A)bằng cách phân bổ tạm thời.

Giải thích khả dĩ nhất về lý do tại sao Armadillo không thực hiện tối ưu hóa này là nó sẽ không chính xác khi làm điều đó.

Cuối cùng, có một cách đơn giản hơn nhiều để làm những gì bạn muốn, như thế này:

cx_mat *A, *Atemp, B;
for (;;) {
  *Atemp = (*A)*B;
  swap(A, Atemp);
}

Điều này giống hệt với

cx_mat A, B;
for (;;) {
  A = A*B;
}

nhưng nó phân bổ một ma trận tạm thời, thay vì một ma trận tạm thời trên mỗi lần lặp.


Đó là cách đơn giản hơn nhiều để thực hiện điều đó - ngoài việc trông có vẻ mơ hồ (mặc dù đúng, hoán đổi thay vì bản sao thực sự là một thành ngữ C ++, may mắn thay, rất ít cần thiết kể từ C ++ 11), và sụp đổ nếu bạn không new-initialise Atemp- hoàn toàn không mang lại cho bạn bất cứ điều gì: nó vẫn liên quan đến việc tạo ra một ma trận tạm thời mới (*A)*Bvà sao chép nó vào *Atemp, trừ khi RVO ngăn chặn nó.
leftaroundabout

1
@leftaroundabout Không, nếu một ví dụ tạm thời được tạo ra trong ví dụ của tôi, thì đó là lỗi Armadillo. Các thư viện đại số tuyến tính dựa trên các mẫu biểu thức rõ ràng tránh tạo ra các thời gian trong các kết quả trung gian. Giá trị của (*A)*Bkhông một ma trận tạm thời, nhưng một đối tượng biểu hiện mà theo dõi những biểu hiện và các đầu vào của nó. Tôi đã cố gắng giải thích tại sao tối ưu hóa này không kích hoạt trong ví dụ ban đầu và nó không liên quan gì đến RVO (hoặc di chuyển ngữ nghĩa như trong câu trả lời khác). Tôi đã bỏ qua tất cả các mã khởi tạo, nó không quan trọng trong ví dụ, tôi chỉ hiển thị các loại.
Kirill

Ok, tôi thấy những gì bạn đang nhận được, nhưng điều này vẫn có vẻ là một cách rất đáng tin cậy, không đáng tin cậy để làm điều đó. Nếu các nhà thiết kế đã đưa ra tùy chọn để tối ưu hóa phép nhân phá hoại theo cách này, chắc chắn họ đã thực hiện nó bằng một phương pháp chuyên dụng hoặc ít nhất là cung cấp một tùy chỉnh swapđể bạn không phải thực hiện kiểu tung hứng con trỏ này.
leftaroundabout

1
@leftaroundabout Ngoài ra, ví dụ không hoán đổi ma trận, nó hoán đổi con trỏ thành ma trận, để tránh mọi sự sao chép. Có hai ma trận tạm thời, và một trong số chúng được coi là ma trận tạm thời chuyển đổi mỗi lần lặp.
Kirill

2
@leftaroundabout: Không có quản lý bộ nhớ đang diễn ra ở đây với việc sử dụng con trỏ này. Nó chỉ là một khối mã nhỏ nơi bạn có hai đối tượng và cần theo dõi đối tượng bạn đang sử dụng cho mục đích gì.

8

@BillGreene chỉ ra "tối ưu hóa giá trị trả về" như một cách giải quyết vấn đề cơ bản, nhưng điều này thực sự chỉ giúp cho một nửa của nó. Giả sử bạn có mã của mẫu này:

struct ExpensiveObject { ExpensiveObject(); ~ExpensiveObject(); };

ExpensiveObject operator+ (ExpensiveObject &obj1,
                           ExpensiveObject &obj2)
{
   ExpensiveObject tmp;
   ...compute tmp based on obj1 and obj2...
   return tmp;
}

void f() {
  ExpensiveObject o1, o2, o3;
  ...initialize o1, o2...;
  o3 = o1 + o2;
}

Một trình biên dịch ngây thơ sẽ

  1. tạo một vị trí để lưu trữ kết quả của phép toán cộng (một tạm thời),
  2. gọi tổng đài +,
  3. tạo đối tượng 'tmp' bên trong toán tử + (tạm thời thứ hai),
  4. tính toán tmp,
  5. sao chép tmp vào khe kết quả,
  6. phá hủy tmp,
  7. sao chép đối tượng kết quả vào o3
  8. phá hủy đối tượng kết quả

Tối ưu hóa giá trị trả về chỉ có thể thống nhất đối tượng 'tmp' và vị trí 'kết quả', nhưng không thể loại bỏ sự cần thiết phải sao chép. Vì vậy, bạn vẫn còn lại với việc tạo ra một hoạt động tạm thời, sao chép và phá hủy tạm thời.

Cách duy nhất xung quanh điều này là toán tử + không trả về một đối tượng, mà là một đối tượng của một lớp trung gian nào đó, khi được gán cho một ExpensiveObject, thực hiện thao tác thêm và sao chép tại chỗ. Đây là cách tiếp cận điển hình được sử dụng trong các thư viện mẫu biểu thức .


Cảm ơn bạn cho thông tin này. Bạn có thể cung cấp một ví dụ mà tôi có thể sử dụng với Armadillo để tránh vấn đề này không?
Nhà vật lý lượng tử

Và tôi muốn hỏi: Đây là một vấn đề triển khai ở Armadillo, phải không? Ý tôi là nó không thực sự thông minh để làm theo cách này ... ít nhất là họ phải đưa kết quả cho tùy chọn tham chiếu. Đúng?
Nhà vật lý lượng tử

6
Phần quan trọng của câu trả lời này là kết thúc. Armadillo sử dụng các mẫu biểu thức để đánh giá các biểu thức một cách lười biếng khi có thể. Điều đó cắt giảm số lượng thời gian được tạo ra. Điều chính mà OP nên ghi nhớ là chạy một trình lược tả để xác định nơi xảy ra sự chậm lại, sau đó tập trung vào việc tối ưu hóa những điều đó. Thông thường, các lý thuyết về mã "nên chậm" hóa ra không đúng.
Jason R

Tôi không tin bất kỳ thời gian nào được tạo cho ví dụ này khi được biên dịch bằng trình biên dịch C ++ hiện đại. Tôi đã tạo một ví dụ đơn giản cho thấy điều này và cập nhật bài viết của tôi. Nói chung, tôi không đồng ý với giá trị của kỹ thuật mẫu biểu thức, nhưng nó không liên quan đến một biểu thức toán tử đơn giản, giống như biểu thức được trình bày ở trên.
Bill Greene

@BillGreene: Tạo một lớp với hàm tạo, sao chép hàm tạo, toán tử gán và hàm hủy và biên dịch ví dụ. Bạn sẽ thấy rằng tạm thời được tạo ra. Ngoài ra: cần phải được tạo bởi vì trình biên dịch không thể loại bỏ nó mà không hợp nhất toán tử sao chép, hàm tạo và hàm hủy. Điều đó đơn giản là không thể đối với các hoạt động không tầm thường như cấp phát bộ nhớ.
Wolfgang Bangerth

5

Stackoverflow ( https://stackoverflow.com/ ) có lẽ là một diễn đàn thảo luận tốt hơn cho câu hỏi này. Tuy nhiên, đây là một câu trả lời ngắn.

Tôi nghi ngờ rằng trình biên dịch C ++ đang tạo mã cho biểu thức này giống như bạn mô tả ở trên. Tất cả các trình biên dịch C ++ hiện đại đều thực hiện tối ưu hóa gọi là "tối ưu hóa giá trị trả về" ( http://en.wikipedia.org/wiki/Return_value_optimization ). Với tối ưu hóa giá trị trả về, kết quả evolutionMatrix*stateMatrixđược lưu trữ trực tiếp trong stateMatrix; không có bản sao được thực hiện.

Rõ ràng có sự nhầm lẫn đáng kể về chủ đề này và đó là một trong những lý do tôi đề xuất Stackoverflow có thể là một diễn đàn tốt hơn. Có rất nhiều "luật sư ngôn ngữ" C ++ ở đó trong khi hầu hết chúng ta ở đây muốn dành thời gian cho CSE. ;-)

Tôi đã tạo ra ví dụ đơn giản sau dựa trên bài của Giáo sư Bangerth:

#ifndef NDEBUG
#include <iostream>

using namespace std;
#endif

class ExpensiveObject  {
public:
  ExpensiveObject () {
#ifndef NDEBUG
    cout << "ExpensiveObject  constructor called." << endl;
#endif
    v = 0;
  }
  ExpensiveObject (int i) { 
#ifndef NDEBUG
    cout << "ExpensiveObject  constructor(int) called." << endl;
#endif
    v = i; 
  }
  ExpensiveObject (const ExpensiveObject  &a) {
    v = a.v;
#ifndef NDEBUG
    cout << "ExpensiveObject  copy constructor called." << endl;
#endif
  }
  ~ExpensiveObject() {
#ifndef NDEBUG
    cout << "ExpensiveObject  destructor called." << endl;
#endif
  }
  ExpensiveObject  operator=(const ExpensiveObject  &a) {
#ifndef NDEBUG
    cout << "ExpensiveObject  assignment operator called." << endl;
#endif
    if (this != &a) {
      return ExpensiveObject (a);
    }
  }
  void print() const {
#ifndef NDEBUG
    cout << "v=" << v << endl;
#endif
  }
  int getV() const {
    return v;
  }
private:
  int v;
};

ExpensiveObject  operator+(const ExpensiveObject  &a1, const ExpensiveObject  &a2) {
#ifndef NDEBUG
  cout << "ExpensiveObject  operator+ called." << endl;
#endif
  return ExpensiveObject (a1.getV() + a2.getV());
}

int main()
{
  ExpensiveObject  a(2), b(3);
  ExpensiveObject  c = a + b;
#ifndef NDEBUG
  c.print();
#endif
}

Có vẻ phức tạp hơn thực tế là vì tôi muốn xóa hoàn toàn tất cả mã để in đầu ra khi biên dịch ở chế độ tối ưu hóa. Khi tôi chạy phiên bản được biên dịch với tùy chọn gỡ lỗi, tôi nhận được kết quả đầu ra sau:

ExpensiveObject  constructor(int) called.
ExpensiveObject  constructor(int) called.
ExpensiveObject  operator+ called.
ExpensiveObject  constructor(int) called.
v=5
ExpensiveObject  destructor called.
ExpensiveObject  destructor called.
ExpensiveObject  destructor called.

Điều đầu tiên cần lưu ý là không có thời gian được xây dựng - chỉ có a, b và c. Hàm tạo mặc định và toán tử gán không bao giờ được gọi vì chúng không cần trong ví dụ này.

Giáo sư Bangerth đã đề cập các mẫu biểu thức. Thật vậy, kỹ thuật tối ưu hóa này rất quan trọng trong việc đạt được hiệu suất tốt trong thư viện lớp ma trận. Nhưng điều quan trọng chỉ khi các biểu thức đối tượng phức tạp hơn đơn giản là a + b. Nếu, ví dụ, thử nghiệm của tôi là:

  ExpensiveObject  a(2), b(3), c(9);
  ExpensiveObject  d = a + b + c;

Tôi sẽ nhận được đầu ra sau đây:

ExpensiveObject  constructor(int) called.
 ExpensiveObject  constructor(int) called.
 ExpensiveObject  constructor(int) called.
 ExpensiveObject  operator+ called.
 ExpensiveObject  constructor(int) called.
 ExpensiveObject  operator+ called.
 ExpensiveObject  constructor(int) called.
 ExpensiveObject  destructor called.
 v=14
 ExpensiveObject  destructor called.
 ExpensiveObject  destructor called.
 ExpensiveObject  destructor called.
 ExpensiveObject  destructor called.

Trường hợp này cho thấy việc xây dựng tạm thời không mong muốn (5 cuộc gọi đến nhà xây dựng và hai cuộc gọi của nhà điều hành +). Việc sử dụng đúng các mẫu biểu thức (một chủ đề nằm ngoài phạm vi của diễn đàn này) sẽ ngăn chặn điều này tạm thời. (Đối với những người có động lực cao, có thể tìm thấy một cuộc thảo luận đặc biệt dễ đọc về các mẫu biểu thức trong chương 18 của http://www.amazon.com/C-Temsheet-The-Complete-Guide/dp/0201734842 ).

Cuối cùng, "bằng chứng" thực sự về những gì trình biên dịch đang thực sự xuất phát từ việc kiểm tra đầu ra mã lắp ráp của trình biên dịch. Đối với ví dụ đầu tiên, khi được biên dịch trong chế độ tối ưu hóa, mã này đơn giản đến mức đáng kinh ngạc. Tất cả các lệnh gọi hàm đã được tối ưu hóa và mã lắp ráp về cơ bản tải 2 vào một thanh ghi, 3 thành một giây và thêm chúng vào.


Tôi đã thực sự do dự khi đặt nó ở đây hoặc trên stackoverflow ... Tôi khá chắc chắn nếu tôi đã đặt nó trên stackoverflow, ai đó sẽ nhận xét rằng tôi nên đặt nó ở đây :-). Dù sao; tối ưu hóa giá trị trả về là tin tốt và tôi không biết điều đó trước đây (+1). Cảm ơn vì điều đó. Thật không may, tôi không biết bất cứ điều gì trong mã lắp ráp, vì vậy đó không phải là kiểm tra mà tôi có thể làm.
Nhà vật lý lượng tử

1
Nếu tôi không nhầm, thậm chí xem xét tối ưu hóa giá trị trả về, trình biên dịch sẽ hoạt động với ba ma trận trong bộ nhớ chứ không phải hai. "Nhân A và B, và đặt kết quả vào C" là một hàm khác với "nhân A và B, và ghi đè B bằng kết quả".
Federico Poloni

Điểm thú vị. Tôi đã tập trung vào mong muốn của người đăng để có một triển khai nhân ma trận hiệu quả như hàm bội () của anh ta nhưng với sự quá tải tốt đẹp của toán tử nhân. Có cách nào để thực hiện một ma trận tổng quát nhân mà không cần ba ma trận không? RVO, tất nhiên, loại bỏ sự cần thiết phải có một bản sao của ma trận đầu ra.
Bill Greene

Tài liệu tham khảo của @ BillGreene để trả về tối ưu hóa giá trị chỉ tránh được nhu cầu tạm thời thứ hai, nhưng vẫn cần một thứ. Tôi sẽ bình luận về điều này trong một câu trả lời khác.
Wolfgang Bangerth

1
@BillGreene: Ví dụ của bạn quá đơn giản. Tối ưu hóa đi một số bài tập, tạo tạm thời, v.v., là có thể bởi vì không có tác dụng phụ nào mà trình biên dịch phải điều chỉnh. Về bản chất, bạn chỉ đang làm việc trên một vô hướng. Hãy thử một ví dụ trong đó thay vì một vô hướng duy nhất, lớp yêu cầu phân bổ và xóa bộ nhớ. Trong trường hợp này, bạn phải gọi mallocfreetrình biên dịch không thể tối ưu hóa các cặp trong số chúng mà không vấp màn hình bộ nhớ, v.v.
Wolfgang Bangerth

5

O(n2.8)O(n2)n

Đó là, trừ khi bạn phải chịu một hằng số khổng lồ vào việc sao chép - mà thực sự là không quá xa vời, vì phiên bản với việc sao chép đắt hơn nhiều về vấn đề khác: nó cần nhớ cách hơn. Vì vậy, nếu bạn cuối cùng phải trao đổi sang và từ đĩa cứng, việc sao chép thực sự có thể trở thành nút cổ chai. Tuy nhiên, ngay cả khi bạn không tự sao chép bất cứ điều gì, một thuật toán song song mạnh mẽ cũng có thể thực hiện một số bản sao của chính nó. Thực sự, cách duy nhất để đảm bảo không có quá nhiều bộ nhớ sẽ được sử dụng trong mỗi bước là phân chia phép nhân trong các cột củastateMatrix , do đó chỉ có các phép nhân nhỏ được thực hiện tại một thời điểm. Chẳng hạn, bạn có thể định nghĩa

class HChunkMatrix // optimised for destructive left-multiplication updates
{
  std::vector<arma::cx_mat> colChunks; // e.g. for an m×n matrix,
                                      //  use √n chunks, each an m×√n matrix
 public:
  ...

  HChunkMatrix& operator *= (const arma::cx_mat& lhMult) {
    for (&colChunk: colChunks) {
      colChunk = lhMult * colChunk;
    }
    return *this;
  }
}

Bạn cũng nên xem xét liệu bạn thậm chí có cần phải tiến hóa stateMatrixnhư là một trong những nơi đầu tiên. Nếu về cơ bản bạn chỉ muốn tiến hóa thời gian độc lập của các nkets nhà nước, thì bạn cũng có thể tiến hóa từng cái một, điều này ít tốn kém hơn về bộ nhớ. Đặc biệt nếu evolutionMatrixthưa thớt , mà bạn chắc chắn nên kiểm tra! Đối với điều này về cơ bản chỉ là một Hamilton, phải không? Người Hamilton thường thưa thớt hoặc gần như thưa thớt.


O(n2.38)


1
Đây là câu trả lời tốt nhất; những người khác bỏ lỡ điểm quan trọng là phép nhân ma trận thực sự không phải là thứ bạn làm tại chỗ.

5

C ++ hiện đại có một giải pháp cho vấn đề bằng cách sử dụng "hàm tạo di chuyển" và "tham chiếu giá trị".

Một "constructor di chuyển" là một hàm tạo cho một lớp, ví dụ như một lớp ma trận, lấy một thể hiện khác của cùng một lớp và chuyển dữ liệu từ thể hiện khác sang thể hiện mới, để trống thể hiện ban đầu. Thông thường, một đối tượng ma trận sẽ có hai số cho kích thước và một con trỏ tới dữ liệu. Khi một hàm tạo bình thường sẽ nhân đôi dữ liệu, một hàm tạo di chuyển sẽ chỉ sao chép hai số và con trỏ, vì vậy điều này thực sự nhanh.

Một "tham chiếu giá trị", được viết ví dụ là "ma trận &&" thay vì "ma trận &" thông thường được sử dụng cho các biến tạm thời. Bạn sẽ khai báo phép nhân ma trận khi trả về ma trận &&. Bằng cách đó, trình biên dịch sẽ đảm bảo rằng một hàm tạo di chuyển rất rẻ sẽ được sử dụng để lấy kết quả ra khỏi hàm gọi nó. Vì vậy, một biểu thức như result = (a + b) * (c + d) trong đó a, b, c, d là các đối tượng ma trận khổng lồ, sẽ xảy ra mà không cần sao chép.

Googling cho "tham chiếu giá trị và các nhà xây dựng di chuyển" sẽ tìm thấy các ví dụ và hướng dẫn.


0

vMMMMMMMMMMv

Sau đó, một lần nữa, tôi tập hợp OpenBLAS có một bộ sưu tập tối ưu hóa kiến ​​trúc cụ thể lớn hơn, vì vậy Eigen có thể hoặc không thể là một chiến thắng cho bạn. Thật không may, không có thư viện đại số tuyến tính tuyệt vời đến mức bạn thậm chí không phải xem xét những người khác khi chiến đấu cho "10% cuối cùng" của hiệu suất. Wrappers không phải là một giải pháp 100%; hầu hết (tất cả?) trong số họ không thể tận dụng khả năng của người bản địa để hợp nhất các tính toán theo cách này.


lưu ý, có ~ thư viện cụ thể ứng dụng làm công cụ fancier; Tôi nghĩ API của Apple để tổng hợp hình ảnh thực hiện những việc tương tự như eigen, cộng với việc ánh xạ tính toán lên GPU ... Và tôi tưởng tượng các thư viện luồng âm thanh thực hiện tối ưu hóa tương tự ...
Andrew Wagner
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.