SoA vectơ trên SPU


8

Tôi đã đọc rất nhiều về lợi ích của việc tổ chức dữ liệu vào 'Structs of Arrays' (SoA) thay vì 'Array of Structs' (AoS) điển hình để có được thông lượng tốt hơn khi sử dụng các hướng dẫn SIMD . Trong khi 'tại sao' hoàn toàn có ý nghĩa với tôi, tôi không chắc sẽ làm được bao nhiêu khi làm việc với những thứ như vectơ.

Các vectơ có thể được coi là một cấu trúc của một mảng dữ liệu (kích thước cố định), do đó bạn có thể chuyển đổi một mảng của chúng thành một cấu trúc của các mảng X, Y và Z. Thông qua đó, bạn có thể làm việc trên 4 vectơ cùng một lúc trái ngược với từng vectơ.

Bây giờ, vì lý do cụ thể tôi sẽ đăng bài này lên GameDev:

Điều này có ý nghĩa khi làm việc với các vectơ trên SPU không? Cụ thể hơn, nó có ý nghĩa với DMA nhiều mảng chỉ cho một vectơ không? Hoặc sẽ tốt hơn nếu gắn bó với DMAing mảng vectơ và hủy kết nối chúng vào các thành phần khác nhau để làm việc?

Tôi có thể thấy lợi ích của việc cắt bỏ việc không kiểm soát (nếu bạn đã thực hiện 'AoS'), nhưng có vẻ như bạn có thể nhanh chóng hết các kênh DMA nếu bạn thực hiện tuyến đường này và làm việc với nhiều bộ vectơ cùng một lúc.

(Lưu ý: chưa có kinh nghiệm chuyên môn với Cell, nhưng đã chơi đùa trong OtherOS một thời gian)

Câu trả lời:


5

Một cách tiếp cận là sử dụng cách tiếp cận AoSoA (đọc: Array of Struct of Array), là sự kết hợp giữa AoS và SoA. Ý tưởng là lưu trữ N structs giá trị dữ liệu trong một đoạn liền kề ở dạng SoA, sau đó là N structs tiếp theo có giá trị ở dạng SoA.

Biểu mẫu AoS của bạn cho 16 vectơ (được dán nhãn 0,1,2 ... F), xuất hiện ở mức độ chi tiết của 4 cấu trúc là:

000111222333444555666777888999AAABBBCCCDDDEEEFFF
XYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZXYZ

đối với SoA, đây là:

0123456789ABCDEF
XXXXXXXXXXXXXXXXX

0123456789ABCDEF
YYYYYYYYYYYYYYY

0123456789ABCDEF
CÂU CHUYỆN

đối với AoSoA, điều này trở thành:

01230123012345674567456789AB89AB89ABCDEFCDEFCDEF
XXXXYYYYZZZZXXXXYYYYZZZZXXXXYYYYZZZZXXXXYYYYZZ

Cách tiếp cận AoSoA có những lợi ích sau của AoS:

  • Chỉ cần một lần chuyển DMA duy nhất để chuyển một đoạn cấu trúc sang bộ nhớ cục bộ SPU.
  • cấu trúc vẫn có cơ hội tất cả dữ liệu phù hợp trong một bộ đệm.
  • Khối tìm nạp trước vẫn rất dễ dàng.

Cách tiếp cận AoSoA cũng có những lợi ích của mẫu SoA:

  • Bạn có thể tải dữ liệu từ bộ nhớ cục bộ SPU trực tiếp vào các thanh ghi vector 128 bit mà không cần phải làm xáo trộn dữ liệu của bạn.
  • Bạn vẫn có thể hoạt động trên 4 cấu trúc cùng một lúc.
  • Bạn hoàn toàn có thể sử dụng SIMD'ness của bộ xử lý vector của mình nếu không có phân nhánh cơ bản (nghĩa là không có làn đường không được sử dụng trong số học vector của bạn)

Cách tiếp cận AoSoA vẫn có một số nhược điểm của hình thức SoA:

  • quản lý đối tượng phải được thực hiện tại độ chi tiết xoáy.
  • truy cập ngẫu nhiên ghi một cấu trúc đầy đủ bây giờ cần phải chạm vào bộ nhớ phân tán.
  • (những điều này có thể không phải là vấn đề tùy thuộc vào cách bạn tổ chức / quản lý cấu trúc và thời gian tồn tại của chúng)

BTW, các khái niệm AoSoA này áp dụng rất tốt cho SSE / AVX / LRBni, cũng như các GPU có thể được ví như bộ xử lý SIMD rất rộng, ví dụ. Rộng 32/48/64 tùy theo nhà cung cấp / kiến ​​trúc.


Tôi không thấy cách này mang lại bất kỳ lợi thế nào so với việc không đóng gói chúng cho mỗi thành phần trừ khi bạn đóng gói dữ liệu không phải vectơ mà bạn thực sự sử dụng như phao - mặc dù tôi thấy AoS của bạn loại trừ W, có vẻ không thân thiện với bộ nhớ, tôi đoán trong trường hợp đó có một chiến thắng. Cũng lưu ý rằng SPU không có dòng bộ đệm ngoại trừ giao tiếp với bộ nhớ chính.
Kaj

2
1. Như với tất cả mọi thứ, số dặm của bạn có thể thay đổi tùy thuộc vào dữ liệu / thuật toán / bộ xử lý chính xác của bạn. Trong các trường hợp ràng buộc đăng ký, việc tránh cần 4 thanh ghi tạm thời trước khi bạn có thể trộn tất cả các trường X của mình vào cùng một thanh ghi có thể hữu ích. Nhưng một lần nữa, YMMV. 2. Câu trả lời của tôi tổng quát hơn vì các khái niệm chuyển giao tốt trong lĩnh vực lập trình dữ liệu song song; các cân nhắc về dòng bộ nhớ cache phù hợp hơn với GPU / SSE nhưng tôi cảm thấy mình nên đề cập đến tất cả chúng giống nhau :)
jpaver

1
Đủ công bằng, tôi đứng giác ngộ và sẽ học cách phê bình một cách tinh tế hơn! Cảm ơn đã chia sẻ cái nhìn sâu sắc của bạn: o)
Kaj

3

SPU thực sự là một trường hợp đặc biệt thú vị khi nói đến mã vector. Các hướng dẫn được chia thành các gia đình "số học" và "tải / lưu trữ" và hai gia đình chạy trên các đường ống riêng biệt. SPU có thể phát hành một trong mỗi loại cho mỗi chu kỳ.

Mã toán học rõ ràng bị ràng buộc rất nhiều bởi các hướng dẫn toán học - vì vậy, thông thường các vòng lặp toán học trên SPU sẽ có rất nhiều chu kỳ mở trên đường ống tải / cửa hàng. Vì sự xáo trộn xảy ra trên đường ống tải / cửa hàng, bạn thường có đủ hướng dẫn tải / lưu trữ miễn phí để chuyển mẫu xyzxyzxyzxyz thành dạng xxxxyyyyzzzz mà không cần bất kỳ chi phí nào.

Kỹ thuật này được sử dụng ít nhất tại Chó nghịch ngợm - xem các bài thuyết trình lắp ráp SPU của chúng ( phần 1phần 2 ) để biết chi tiết.

Thật không may, trình biên dịch thường không đủ thông minh để thực hiện việc này một cách tự động - nếu bạn quyết định đi theo con đường này, bạn sẽ cần phải tự lắp ráp hoặc hủy các vòng lặp của mình bằng cách sử dụng nội tại và kiểm tra trình biên dịch để đảm bảo đó là những gì bạn muốn. Vì vậy, nếu bạn đang tìm cách viết mã đa nền tảng chung chạy tốt trên SPU, bạn có thể muốn đi với SoA hoặc AoSoA (như jpaver gợi ý.)


À, chúng tôi đồng ý sau tất cả: o) Lướt trên SPU nếu bạn cần, đủ thời gian để làm điều đó.
Kaj

1

Như với bất kỳ tối ưu, hồ sơ! Khả năng đọc là ưu tiên hàng đầu và chỉ nên hy sinh khi cấu hình xác định một nút cổ chai cụ thể và bạn đã sử dụng hết tất cả các tùy chọn để điều chỉnh thuật toán cấp cao (cách nhanh nhất để thực hiện công việc là không phải thực hiện công việc!) tuân theo bất kỳ tối ưu hóa cấp thấp nào để xác nhận rằng bạn thực sự đã làm mọi thứ nhanh hơn thay vì ngược lại, đặc biệt là với các đường ống kỳ quặc như của Cell.

Những kỹ thuật bạn sử dụng sau đó sẽ phụ thuộc vào các chi tiết của nút cổ chai. Nói chung, khi làm việc với các loại vectơ, một thành phần vectơ bạn bỏ qua trong kết quả thể hiện công việc bị lãng phí. Chuyển đổi SoA / AoS không có ý nghĩa gì trừ khi nó cho phép bạn thực hiện nhiều công việc hữu ích hơn bằng cách điền vào các thành phần không sử dụng đó (ví dụ: một sản phẩm chấm trên PPU của PS3 so với bốn sản phẩm song song trong cùng một khoảng thời gian). Để giải quyết câu hỏi của bạn, dành thời gian xáo trộn các thành phần xung quanh chỉ để thực hiện một thao tác trên một vectơ duy nhất nghe có vẻ bi quan đối với tôi!

Mặt trái của SPU là phần lớn chi phí chuyển DMA nhỏ đang được thiết lập; bất cứ điều gì ít hơn 128 byte sẽ có cùng số chu kỳ để chuyển và bất cứ điều gì ít hơn khoảng một kilobyte chỉ một vài chu kỳ nữa. Vì vậy, đừng lo lắng về việc DMAing nhiều dữ liệu hơn bạn cần; giảm số lần chuyển DMA tuần tự được kích hoạt và thực hiện công việc trong khi chuyển DMA đang diễn ra - và do đó mở ra các phần mở đầu và bản tóm tắt để hình thành các đường ống phần mềm - là chìa khóa để thực hiện SPU tốt và dễ dàng nhất để xử lý các trường hợp góc bằng cách lấy dữ liệu bổ sung / loại bỏ các kết quả được tính toán một phần so với nhảy qua các vòng để cố gắng sắp xếp cho lượng dữ liệu chính xác cần thiết để đọc và xử lý.


Nếu bạn kết thúc việc giải nén chúng, theo cách tiếp cận AOSAO, ít nhất là kéo theo nhiều vectơ cùng một lúc. Ngoài ra, bạn muốn kéo theo một đợt và trong khi xử lý các lần kéo đó trong đợt tiếp theo. Trong khi gửi đợt đầu tiên, bạn xử lý đợt thứ hai và kéo vào đợt thứ ba. Bằng cách đó bạn che giấu độ trễ nhiều nhất có thể.
Kaj

0

Không, điều đó sẽ không có nhiều ý nghĩa nói chung vì hầu hết các vectơ vectơ hoạt động trên một vectơ nói chung và không phải trên các thành phần riêng biệt. Vì vậy, bạn đã có thể nhân một vectơ trong 1 hướng dẫn, trong khi với việc tách các thành phần riêng biệt bạn sẽ dành 4 hướng dẫn cho nó. Vì vậy, về cơ bản bạn thực hiện rất nhiều thao tác nói chung trên một phần của cấu trúc, bạn nên đóng gói chúng trong một mảng, nhưng bạn hầu như không làm mọi thứ chỉ trên một thành phần của một vectơ, hoặc khác biệt lớn trên mỗi thành phần để phá vỡ chúng sẽ không hoạt động.
Tất nhiên, nếu bạn tìm thấy một tình huống mà bạn phải làm gì đó chỉ với các thành phần (nói) x của vectơ thì nó có thể hoạt động, tuy nhiên hình phạt làm phồng mọi thứ trở lại khi bạn cần vectơ thực tế sẽ không rẻ để bạn có thể tự hỏi nếu bạn không nên sử dụng vectơ để bắt đầu nhưng chỉ là một mảng nổi xảy ra để cho phép các vectơ vectơ thực hiện các phép tính cụ thể của chúng.


2
Bạn đang thiếu điểm SoA cho toán học vectơ. Bạn hiếm khi chỉ có một đối tượng bạn đang làm việc - trong thực tế, bạn đang lặp lại một mảng và làm điều tương tự với nhiều đối tượng. Xem xét làm 4 sản phẩm chấm. Nếu bạn đang lưu trữ các vectơ dưới dạng AoS ở dạng xyz0, thì việc lấy dấu chấm của hai vectơ yêu cầu nhân-shuffle-add-shuffle-add - 5 hướng dẫn. Làm 4 sản phẩm chấm cần 20 hướng dẫn. Mặt khác, nếu bạn có 8 vectơ được lưu trữ thời trang SoA (xxxx, yyyy, zzzz, xxxx, yyyy, zzzz), bạn có thể thực hiện các sản phẩm 4 chấm chỉ với 3 hướng dẫn (mul, madd, madd) - nhanh hơn 6 lần.
Charlie

Điểm công bằng. Tuy nhiên, hai quan sát. Tôi sẽ luôn luôn giữ W để tôi không cần 20 hướng dẫn, thứ hai, hầu hết các chi phí còn lại có thể bị ẩn trong độ trễ của các hướng dẫn khác - vòng lặp chặt chẽ của bạn sẽ bị các quầy hàng đường ống nghiêm trọng, phải không? làm 6 lần là tối ưu hóa lý thuyết. Vì vậy, trong khi có, bạn muốn bó các hoạt động của mình - hầu như không bao giờ bạn chỉ cần thực hiện một lô sản phẩm chấm nhanh chóng mà không cần làm gì khác trên dữ liệu đã nói. Chi phí cho việc xáo trộn / phân tán về phía PPU sẽ là quá nhiều hy sinh cho tôi.
Kaj

Rên rỉ, tôi đứng chính xác - trên SPU tôi sẽ cần 20 nếu được thực hiện một cách ngây thơ (nhưng tôi sẽ xáo trộn tại chỗ). Đó là một trong những điều mà cuối cùng tôi đã làm rất nhiều trò xáo trộn để làm cho nó tối ưu. 360 có một dấu chấm nội tại đẹp (nhưng thiếu các thao tác bit tuyệt vời).
Kaj

Vâng, bây giờ tôi nghĩ về nó, nếu bạn đang cố gắng thực hiện "sản phẩm 4 chấm", bạn có thể thực hiện tốt hơn 20 hướng dẫn vì bạn có thể kết hợp một số bổ sung sau. Nhưng có các vectơ của bạn trong các thanh ghi là xxxx, yyyy, zzzz - cho dù bạn đã thay đổi hoặc lưu trữ dưới dạng SoA - sẽ loại bỏ hoàn toàn các xáo trộn đó. Dù sao, bạn đúng khi SoA làm cho mã logic phân nhánh chậm hơn - nhưng tôi cho rằng giải pháp trong nhiều trường hợp như vậy là xô dữ liệu của bạn và cấu trúc lại logic phân nhánh thành các vòng phẳng đẹp.
Charlie

Đã đồng ý. Tôi khá chắc chắn nếu tôi xem qua mã SPU cũ của mình (không thể, công ty trước đó) có những trường hợp tôi đã chuyển nó sang định dạng xxxxyyyyzzzz để tối ưu hóa mà không nhận ra cụ thể. Tôi chưa bao giờ cung cấp nó từ PPU ở định dạng đó. Tâm trí bạn, OP suy ngẫm về dma-ing x, y, z một cách riêng biệt. Điều đó chắc chắn sẽ không làm việc cho tôi. Tôi cũng (như tôi đã làm) thà thay đổi cục bộ vì không phải mọi thứ đều hoạt động tốt hơn ở định dạng xxxxyyyyzzzz. Tôi phải chọn các trận đánh của bạn, tôi đoán. Tối ưu hóa cho SPU là một vụ nổ và bạn cảm thấy cực kỳ thông minh khi bạn có được giải pháp chặt chẽ đó: o)
Kaj
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.