Có đáng để sử dụng các nhóm hạt trong các ngôn ngữ được quản lý không?


10

Tôi sẽ triển khai một nhóm đối tượng cho hệ thống hạt của mình trong Java, sau đó tôi tìm thấy điều này trên Wikipedia. Để định nghĩa lại, nó nói rằng nhóm đối tượng không đáng sử dụng trong các ngôn ngữ được quản lý như Java và C #, vì việc phân bổ chỉ mất hàng chục thao tác so với hàng trăm ngôn ngữ không được quản lý như C ++.

Nhưng như tất cả chúng ta đều biết, mọi hướng dẫn có thể làm giảm hiệu suất trò chơi. Ví dụ: nhóm khách hàng trong MMO: khách hàng sẽ không vào và thoát khỏi nhóm quá nhanh. Nhưng các hạt có thể được làm mới hàng chục lần trong một giây.

Câu hỏi đặt ra là: có đáng để sử dụng một nhóm đối tượng cho các hạt (cụ thể là những hạt chết và được tái tạo nhanh) trong một ngôn ngữ được quản lý không?

Câu trả lời:


14

Vâng, đúng vậy.

Thời gian phân bổ không phải là yếu tố duy nhất. Phân bổ có thể có tác dụng phụ, chẳng hạn như tạo ra một bộ sưu tập rác, điều này không chỉ có thể tác động tiêu cực đến hiệu suất mà còn có thể tác động đến hiệu suất một cách khó lường. Các chi tiết cụ thể sẽ phụ thuộc vào lựa chọn ngôn ngữ và nền tảng của bạn.

Pooling nói chung cũng cải thiện địa phương tham chiếu cho các đối tượng trong nhóm, ví dụ bằng cách giữ tất cả chúng trong các mảng liền kề. Điều này có thể cải thiện hiệu suất trong khi lặp lại nội dung của nhóm (hoặc ít nhất là phần sống của chúng) vì đối tượng tiếp theo trong lần lặp sẽ có xu hướng đã có trong bộ đệm dữ liệu.

Sự khôn ngoan thông thường của việc cố gắng tránh bất kỳ sự phân bổ nào trong các vòng lặp trò chơi trong cùng của bạn vẫn áp dụng ngay cả trong các ngôn ngữ được quản lý (đặc biệt là trên 360, ví dụ như 360 khi sử dụng XNA). Những lý do cho nó chỉ khác nhau một chút.


+1 Nhưng, bạn đã không động đến việc liệu nó có đáng khi sử dụng các cấu trúc hay không: về cơ bản là không (gộp các loại giá trị không đạt được gì) - thay vào đó bạn nên có một mảng (hoặc có thể là một bộ) để quản lý chúng.
Jonathan Dickinson

2
Tôi đã không chạm vào thứ cấu trúc vì OP đã đề cập bằng Java và tôi không quen với cách các loại / cấu trúc giá trị hoạt động trong ngôn ngữ đó.

Không có cấu trúc nào trong Java, chỉ có các lớp (luôn luôn trong heap).
Brendan Long

1

Đối với Java, việc nhóm các đối tượng * không hữu ích lắm vì chu trình GC đầu tiên cho các đối tượng vẫn ở xung quanh sẽ định hình lại chúng trong bộ nhớ, di chuyển chúng ra khỏi không gian "Eden" và có khả năng mất địa phương trong quá trình.

  • Nó luôn hữu ích trong bất kỳ ngôn ngữ nào để tập hợp các tài nguyên phức tạp rất tốn kém để phá hủy và tạo ra như các luồng. Chúng có thể có giá trị gộp vì chi phí tạo và hủy chúng gần như không liên quan gì đến bộ nhớ liên quan đến xử lý đối tượng với tài nguyên. Tuy nhiên, các hạt không phù hợp với thể loại này.

Java cung cấp phân bổ cụm nhanh bằng cách sử dụng bộ cấp phát tuần tự khi bạn phân bổ nhanh các đối tượng vào không gian Eden. Chiến lược phân bổ tuần tự đó là siêu nhanh, nhanh hơnmalloc C vì nó chỉ tập hợp bộ nhớ đã được phân bổ theo kiểu tuần tự thẳng, nhưng nó đi kèm với nhược điểm là bạn không thể giải phóng từng phần bộ nhớ. Đây cũng là một mẹo hữu ích trong C nếu bạn chỉ muốn phân bổ mọi thứ cực nhanh cho một cấu trúc dữ liệu mà bạn không cần phải xóa bất cứ thứ gì khỏi nó, chỉ cần thêm mọi thứ và sau đó sử dụng nó và ném toàn bộ mọi thứ sau đó.

Do nhược điểm này là không thể giải phóng các đối tượng riêng lẻ, Java GC, sau một chu kỳ đầu tiên, sẽ sao chép tất cả bộ nhớ được phân bổ từ không gian Eden sang các vùng bộ nhớ mới bằng cách sử dụng bộ cấp phát bộ nhớ đa năng, chậm hơn, cho phép bộ nhớ được giải phóng trong các khối riêng lẻ trong một chủ đề khác nhau. Sau đó, nó có thể loại bỏ toàn bộ bộ nhớ được phân bổ trong không gian Eden mà không cần bận tâm đến các đối tượng riêng lẻ đã được sao chép và sống ở nơi khác trong bộ nhớ. Sau chu trình GC đầu tiên đó, các đối tượng của bạn có thể bị phân mảnh trong bộ nhớ.

Do các đối tượng cuối cùng có thể bị phân mảnh sau chu kỳ GC đầu tiên đó, nên lợi ích của việc tập hợp đối tượng khi chủ yếu là để cải thiện các mẫu truy cập bộ nhớ (địa phương tham chiếu) và giảm chi phí phân bổ / phân bổ phần lớn bị mất ... rất nhiều rằng bạn sẽ có được địa phương tham chiếu tốt hơn thông thường bằng cách chỉ phân bổ các hạt mới mọi lúc và sử dụng chúng trong khi chúng vẫn còn mới trong không gian Eden và trước khi chúng trở nên "cũ" và có khả năng bị phân tán trong bộ nhớ. Tuy nhiên, điều có thể cực kỳ hữu ích (như có được đối thủ hiệu năng C trong Java) là tránh sử dụng các đối tượng cho các hạt của bạn và gộp dữ liệu nguyên thủy cũ. Ví dụ đơn giản, thay vì:

class Particle
{
    public float x;
    public float y;
    public boolean alive;
}

Làm một cái gì đó như:

class Particles
{
    // X positions of all particles. Resize on demand using
    // 'java.util.Arrays.copyOf'. We do not use an ArrayList
    // since we want to work directly with contiguously arranged
    // primitive types for optimal memory access patterns instead 
    // of objects managed by GC.
    public float x[];

    // Y positions of all particles.
    public float y[];

    // Alive/dead status of all particles.
    public bool alive[];
}

Bây giờ để sử dụng lại bộ nhớ cho các hạt hiện có, bạn có thể làm điều này:

class Particles
{
    // X positions of all particles.
    public float x[];

    // Y positions of all particles.
    public float y[];

    // Alive/dead status of all particles.
    public bool alive[];

    // Next free position of all particles.
    public int next_free[];

    // Index to first free particle available to reclaim
    // for insertion. A value of -1 means the list is empty.
    public int first_free;
}

Bây giờ khi nthhạt chết, để cho phép nó được tái sử dụng, hãy đẩy nó vào danh sách miễn phí như vậy:

alive[n] = false;
next_free[n] = first_free;
first_free = n;

Khi thêm một hạt mới, hãy xem bạn có thể bật một chỉ mục từ danh sách miễn phí không:

if (first_free != -1)
{
     int index = first_free;

     // Pop the particle from the free list.
     first_free = next_free[first_free];

     // Overwrite the particle data:
     x[index] = px;
     y[index] = py;
     alive[index] = true;
     next_free[index] = -1;
}
else
{
     // If there are no particles in the free list
     // to overwrite, add new particle data to the arrays,
     // resizing them if needed.
}

Đây không phải là mã dễ chịu nhất để làm việc, nhưng với điều này, bạn sẽ có thể có được một số mô phỏng hạt rất nhanh với xử lý hạt tuần tự luôn rất thân thiện với bộ đệm vì tất cả dữ liệu hạt sẽ luôn được lưu trữ liên tục. Loại đại diện SoA này cũng làm giảm việc sử dụng bộ nhớ vì chúng ta không phải lo lắng về việc đệm, siêu dữ liệu đối tượng cho phản xạ / công văn động và nó phân tách các trường nóng khỏi các trường lạnh (ví dụ: chúng ta không nhất thiết phải quan tâm đến dữ liệu các trường như màu của hạt trong quá trình vật lý đi qua, thật lãng phí khi chỉ tải nó vào một dòng bộ đệm để không sử dụng nó và đuổi nó đi).

Để làm cho mã dễ làm việc hơn, có thể đáng để viết các thùng chứa có thể thay đổi kích thước cơ bản của riêng bạn lưu trữ các mảng float, mảng số nguyên và mảng booleans. Một lần nữa, bạn không thể sử dụng thuốc generic và ArrayListở đây (ít nhất là từ lần cuối cùng tôi kiểm tra) vì điều đó đòi hỏi các đối tượng được quản lý bởi GC, không phải dữ liệu nguyên thủy liền kề. Chúng tôi muốn sử dụng mảng liền kề int, ví dụ, không phải mảng được quản lý bởi GC Integerkhông nhất thiết phải liền kề sau khi rời khỏi không gian Eden.

Với các mảng kiểu nguyên thủy, chúng luôn được đảm bảo liền kề nhau và do đó bạn có được địa phương tham chiếu cực kỳ mong muốn (để xử lý hạt tuần tự, nó tạo ra một thế giới khác biệt) và tất cả các lợi ích mà nhóm đối tượng dự định cung cấp. Với một mảng các đối tượng, thay vào đó nó hơi giống với một loạt các con trỏ bắt đầu chỉ vào các đối tượng theo kiểu liền kề giả định rằng bạn đã phân bổ tất cả chúng cùng một lúc vào không gian Eden, nhưng sau một chu trình GC, có thể được chỉ ra khắp nơi trong ký ức.


1
Đây là một bài viết hay về vấn đề này và sau 5 năm viết mã java tôi có thể thấy rõ điều đó; Java GC chắc chắn không bị câm, nó cũng không được tạo ra để lập trình trò chơi (vì nó không thực sự quan tâm đến địa phương dữ liệu và công cụ), vì vậy chúng tôi chơi tốt hơn khi nó làm hài lòng: P
Gustavo Maciel
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.