Obiekt tablic czy tablica obiektów?

13

Tworzę grę symulacyjną zarządzania, coś w stylu Roller Coaster Tycoon. Chcę wiedzieć, jaki jest najlepszy sposób na uporządkowanie moich obiektów świata, aby zmaksymalizować wydajność.

Powiedzmy, że mam 5000 osób w mojej grze, mogę:

Stwórz obiekt i przechowuj je w takiej tablicy;

class person() {
    this.x = 0;
    this.y = 0;
    this.thirst = 15;
    this.hunger = 15;
    // etc.. add methods:
    public findPath(int destX, int destY) {
    // and so on
    }

    people = new person[5000];

for (int = 0; i < 5000; i++) {
    people[i] = new person;
    }

A może powinienem zrobić obiekt ludzi, który zawiera wiele tablic bajtowych reprezentujących atrybuty takich osób:

class people() {
    this.hunger = new byte[5000]
    this.thirst = new byte[5000]

    getThirst(int i) {
        return this.thirst[i]
        }

 // and so on....

A może jestem zupełnie poza zasięgiem?

ali_goes_oosh
źródło
Dość interesujące pytanie, zwłaszcza że w 2013 r., Kilkanaście lat po ukazaniu się RCT, pomysł posiadania 5000 widocznych, niezależnych NPC na świecie wydawałby się całkowicie niemożliwy (pomimo postępu technologicznego)
Katana314

Odpowiedzi:

15

Powszechną terminologią jest „struktura tablic” (SOA) i „tablica struktur” (AOS), które pochodzą z C i najczęściej są postrzegane w kategoriach pracy SIMD.

Zazwyczaj podejście AOS jest szybsze, jeśli jest stosowane właściwie, ale SOA jest zwykle łatwiejsza w pracy (i dlatego optymalizuje się pod kątem ważniejszej jakości - czasu rozwoju).

SOA, szczególnie w Javie, oznacza, że ​​twoje dane mogą pozostać mocno zapakowane w pamięci. Możesz iterować po właściwościach i oczekiwać, że pamięć podręczna procesora pozostanie szczęśliwa. W systemie AOS, zwłaszcza w Javie, każdy obiekt zostaje ostatecznie przydzielony „gdzieś” w pamięci. Iteracja po obiektach może potencjalnie dość mocno zepsuć pamięć podręczną procesora.

Na koniec wybrałbym dowolne podejście, które uważasz za najłatwiejsze w użyciu. Twój czas programowania jest znacznie cenniejszy niż to, czy gra obsługuje komputery 10-letnie, czy tylko 9-letnie (jest mało prawdopodobne, abyś robił coś, co wymaga najnowszego sprzętu).

Sean Middleditch
źródło
1
Czy w swoim trzecim akapicie masz na myśli dwa razy odwołać się do AOS? Komentarze wydają się sprzeczne ...
ali_goes_oosh
Przepraszamy, naprawiłem to.
Sean Middleditch,
4

Nie ma żadnego powodu, dla którego nie można mieć obu, używając wzoru Fasada do tłumaczenia z jednego interfejsu na drugi reprezentacyjny obraz . Na przykład, używając terminów Sean SOA / AOS:

Fasada SOA

class PeopleFacade {
    Person persons[5000];
    getThirst(int i) { return persons[i].thirst; }
}

Fasada AOS

class People { int thirsts[5000]; } people;
class PersonFacade {
    int i;
    getThirst() { return people.thirsts[i]; }
}

W ten sposób możesz swobodnie wybierać między formą, z której wygodnie korzystasz , jako interfejsem programisty, a tym, co jest najlepsze jako implementacja z dowolnego powodu, w tym z powodu wydajności / pamięci podręcznej.

Kolejną zaletą fasady jest to, że bardzo naturalnie prowadzi ona do wzoru Flyweight , w którym używasz interfejsu do reprezentowania znacznie większej liczby osób niż w rzeczywistości. Na przykład, być może masz robotycznych klientów, którzy nigdy nie są spragnieni; możesz umieścić w tym specjalnym przypadku PersonFacade, a użytkownicy tego interfejsu nigdy nie będą musieli wiedzieć o robotach:

class People { int nonRobotThirsts[1000]; } people;
class PersonFacade {
    int i;
    bool isRobot;
    getThirst() {
        if (isRobot)
            return 0;
        else
            return people.nonRobotThirsts[i];
    }
}

... lub stosując podejście bardziej otwarte, będziesz mieć osobną Robotklasę, która działa dokładnie jak Personwyjątek getThirst().

congusbongus
źródło
-1

Twórz obiekty i przechowuj je w tablicy! Tworzenie tablic na głód i pragnienie może zaoszczędzić trochę miejsca i działać szybciej w niektórych prostych sytuacjach, ale nie jest to OOP. Java i OOP zrobią dla Ciebie wiele, jeśli dasz im szansę. W przypadku naprawdę prostej gry drugi przykład może działać dobrze, ale nawet wtedy powinieneś ćwiczyć swoje umiejętności OO. Twoje pierwsze podejście będzie dla ciebie dobre bez względu na to, jak duży, złożony i włochaty jest twój program.

Pomyśl o wszystkich czasach, w których przydatne będzie odzyskanie Personobiektu z zapytania. Kto wysłał tę wiadomość? na przykład. Wiele metod, które piszesz, chce wiedzieć, z kim mają do czynienia. Będziesz miał wiele metod, które dobrze pasują do odpowiedniej Personklasy. Jeśli Personjest statyczny lub singleton, gdzie stosujesz metody, które działają na poszczególnych ludzi?

Jeśli kiedykolwiek zrobisz wielowątkowość - a przy 5000 użytkowników możesz zostać do tego zmuszony - przekonasz się, że instancja Parent dla każdego użytkownika jest o wiele bardziej praktyczna.

(I ta grupa ludzi: trzymaj się tego na razie, ale w pewnym momencie będziesz chciał innych urządzeń pamięci. Mapa jakiegoś rodzaju, abyś mógł znaleźć ludzi według nazwiska. A może kilka list z różnymi kluczami i prawdopodobnie kilka wyświetla listę na tyle krótką, że może być tablicą lub listami połączonymi).

RalphChapin
źródło