2016-09-28 18 views
6

Bối cảnh

Trong phát triển công cụ trò chơi, chúng tôi thường sử dụng thiết kế hướng dữ liệu để có hiệu suất tính toán và bộ nhớ tối ưu.Làm cách nào để triển khai thiết kế theo hướng dữ liệu trong Rust?

Hãy lấy hệ thống hạt làm ví dụ.

Trong một hệ thống hạt, chúng tôi có rất nhiều hạt, và mỗi hạt có thể có nhiều thuộc tính như vị trí, vận tốc, vv

Một thực hiện điển hình trong C++ sẽ là như thế này:

struct Particle { 
    float positionX, positionY, positionZ; 
    float velocityX, velocityY, velocityZ; 
    float mass; 
    // ... 
}; 

struct ParticleSystem { 
    vector<Particle> particles; 
    // ... 
}; 

Một vấn đề của việc thực hiện này là các thuộc tính hạt được xen kẽ với nhau. Bố cục bộ nhớ này không thân thiện với bộ nhớ cache và có thể không phù hợp với tính toán SIMD.

Thay vào đó trong một thiết kế theo định hướng dữ liệu, chúng ta viết đoạn mã sau:

struct ParticleAttribute { 
    size_t size; 
    size_t alignment; 
    const char* semantic; 
}; 

struct ParticleSystem { 
    ParticleSystem(
     size_t numParticles, 
     const ParticleAttribute* attributes, 
     size_t bufferSize) { 
     for (size_t i = 0; i < numAttributes; ++i) { 
      bufferSize += attributes[i].size * numParticles; 
      // Also add paddings to satisfy the alignment requirements. 
     } 
     particleBuffer = malloc(bufferSize); 
    } 

    uint8* getAttribute(const char* semantic) { 
     // Locate the semantic in attributes array. 
     // Compute the offset to the starting address of that attribute. 
    } 

    uint8* particleBuffer;  
}; 

Bây giờ chúng ta chỉ có một phân bổ và mỗi thuộc tính nằm trong bộ nhớ liên tục. Để mô phỏng các hạt, chúng tôi có thể viết mã sau:

symplecticEuler(ps.getAttribute("positionX"), ps.getAttribute("velocityX"), dt); 

Chức năng getAttribute sẽ nhận được địa chỉ bắt đầu của một thuộc tính cụ thể.

Câu hỏi

Tôi muốn biết cách triển khai điều này trong Rust.

Ý tưởng của tôi trước hết là tạo một lớp được gọi là ParticleSystem, mất một số ParticleAttribute s để tính tổng kích thước bộ đệm, rồi cấp phát bộ nhớ cho bộ đệm. Tôi nghĩ rằng điều này có thể được thực hiện trong mã Rust an toàn.

Bước tiếp theo là triển khai hàm getAttribute, trong đó sẽ trả về tham chiếu đến địa chỉ xuất phát của một thuộc tính cụ thể. Tôi cần sự giúp đỡ của bạn ở đây. Làm cách nào để lấy địa chỉ thô có độ lệch và đưa nó vào loại mong muốn (chẳng hạn như float *) và quấn con trỏ thô đó vào tham chiếu có thể thay đổi trong Rust? Ngoài ra, tôi nghĩ tôi nên quấn con trỏ thô đó vào một tham chiếu có thể thay đổi thành mảng vì tôi cần sử dụng libD của SIM để tải bốn phần tử thông qua tham chiếu đó. Làm cách nào để đạt được điều này bằng cách sử dụng Rust?


Cập nhật: cung cấp thêm thông tin về các thuộc tính. Số lượng và thông tin chi tiết của các thuộc tính được xác định trong thời gian chạy. Các loại thuộc tính có thể khác nhau, nhưng tôi nghĩ chúng ta chỉ phải hỗ trợ các thuộc tính nguyên thủy (f32, f64, ints, ...).

Trả lời

8

Đó là một cách rất phức tạp trong việc triển khai DOD, và ý tưởng sử dụng tra cứu thời gian chạy cho getters khiến tôi khó chịu.

Phiên bản đơn giản là chỉ cần có một phân bổ bộ nhớ cho mỗi thuộc tính:

struct Particles { 
    x: Vec<f32>, 
    y: Vec<f32>, 
} 

mà đòi hỏi phải biết các thuộc tính trước đó.

Sau đó, không có shenanigan để nhận được tất cả các y, họ chỉ ngồi đó, đã gõ, đang chờ bạn.


Mở rộng này để tự động xác định các thuộc tính không phải là phức tạp:

  • chúng ta có thể sử dụng một HashMap<String, xxx> nhìn lên một thuộc tính nhất định tại thời gian chạy
  • chúng ta có thể sử dụng một enum có một đơn Value được lưu trữ trong bản đồ băm có thể có nhiều dạng khác nhau (giải pháp khác sẽ sử dụng một đặc điểm)

này trở thành:

#[derive(Debug, Hash, PartialEq, Eq)] 
enum Value { 
    UniformInt(i64), 
    UniformFloat32(f32), 
    UniformFloat64(f64), 
    DistinctInt(Vec<i64>), 
    DistinctFloat32(Vec<f32>), 
    DistinctFloat64(Vec<f64>), 
} 

struct Particles { 
    store: HashMap<String, Value>, 
} 

chúng ta cũng có thể sử dụng 6 băm-bản đồ ... nhưng trừ khi ai biết một tiên nghiệm gì loại là (khi chỉ nghĩ người ta là một chuỗi), sau đó người ta phải xem xét thông qua tất cả các hashmaps cùng một lúc: gây phiền nhiễu, và lãng phí thời gian.

+0

Cảm ơn bạn đã trả lời nhanh. Tôi đến cách triển khai DOD phức tạp này vì tính linh hoạt trong các thuộc tính là bắt buộc. Thông thường, thông tin thuộc tính chi tiết (nghĩa là số thuộc tính và ngữ nghĩa chính xác) xuất phát từ các nghệ sĩ. Nói cách khác, thông tin này là động mà không thể được xác định trước khi chạy. – TheBusyTypist

+0

@TheBusyTypist: Tôi hiểu rồi ... hãy chỉnh sửa câu hỏi với các chi tiết liên quan sau đó. Cũng nên biết liệu bạn chỉ cần hỗ trợ 'f32' (trong trường hợp đó một' HashMap > 'sẽ hoạt động khá tốt) hoặc nếu bạn cần hỗ trợ nhiều loại. –

+0

Tôi phải hỗ trợ các kiểu nguyên thủy khác như 'f64' hoặc' int '(đối với ID hạt). Như bạn đã đề xuất, bây giờ tôi nghĩ rằng các thuộc tính nhóm trong 'HashMap' riêng biệt theo loại của chúng có thể là một giải pháp hợp lệ. – TheBusyTypist

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