Kiedy można używać macierzy równoległych?

14

Wpadłem na kod (nowy kod), który używa tego, co nazywam „macierzami równoległymi” lub listami. Oznacza to, że istnieją 2 tablice, które zawierają powiązane dane i są powiązane ich pozycją (indeksem) w tablicy.

Uważam to za mylące i podatne na wszelkiego rodzaju błędy. Rozwiązaniem, które zwykle proponuję, jest utworzenie obiektu wywoływanego Companyz polami CompanyId i CompanyName.

Bardzo prawdziwy przykład:

List<string> companyNames;
List<int> companyIds;

//...They get populated somewhere and we then process

for(var i=0; i<companyNames.Count; i++)
{
    UpdateCompanyName(companyIds[i],companyNames[i]);
}

Czy te równoległe tablice są uważane za złą praktykę ?

GER
źródło
9
Po prostu kolejny dowód na to, że nie wynaleziono żadnego języka, w którym nie można pisać w języku Fortran.
andy mango
3
Robienie czegoś takiego może przynieść (całkiem znaczące) buforowanie (choć potrzebne są ciągłe tablice niepołączone z listami), co stało się dość popularne w programowaniu gier związanych z „projektowaniem zorientowanym na dane”. Nie wydaje się to jednak dotyczyć twojej sprawy. Nie wygląda na to, że tworzysz krytyczny kod wydajności.
Derek Elkins opuścił SE
2
@DerekElkins ... Ciekawe, że twój komentarz następuje po jednym, porównując to z kodem Fortran. Wczesne wersje Fortran nie obsługiwały struktur zdefiniowanych przez użytkownika, a nawet po dodaniu idiomatyczny kod Fortran używa wielu tablic właściwości, a nie tablic struktur. Często przypisuje się to temu, że Fortran jest często uważany za najszybszy język.
Jules
3
Myśl styczna do tego pytania: wiele języków funkcjonalnych aktywnie zachęca do pracy z takimi listami. Mają funkcję, zwykle nazywaną zip, która konwertuje je na listę krotek. Twój kod wygląda jak C #. Najnowsza wersja C # dodała obsługę krotek pierwszej klasy. Zastanawiam się, czy w związku z tym dodali gdzieś funkcję zip, która mogłaby automatycznie umieścić twoje listy w użytecznej strukturze?
Jules
4
Cóż, czasami istnieją powody, by używać dwóch tablic celowo, ale w 99% wszystkich przypadków widziałem to, jedynym powodem było lenistwo oryginalnego autora, aby wprowadzić obejmującą strukturę danych.
Doc Brown

Odpowiedzi:

23

Oto kilka powodów, dla których ktoś może użyć tablic parrel:

  1. W języku, który nie obsługuje klas ani struktur
  2. Aby uniknąć blokowania wątków, gdy pojedyncze wątki modyfikują tylko jedną z kolumn
  3. Gdy metoda utrwalania zmusza te rzeczy do osobnego przechowywania, a Ty je odtwarzasz.
  4. Mogą wypełniać mniej pamięci, jeśli struktury są wypełnione. (nie dotyczy tych typów danych w C #)
  5. Gdy części danych muszą być trzymane blisko siebie, aby efektywnie wykorzystać pamięć podręczną procesora (nie przydałoby się to w powyższym kodzie).
  6. Wykorzystanie kodów operacji pojedynczej instrukcji wielu danych (SIMD). (nie dotyczy tego kodu lub ciągów w ogóle)

Nie widzę żadnego ważnego powodu, aby to zrobić w tym przypadku ... i prawdopodobnie są lepsze opcje we wszystkich powyższych przypadkach lub nie są tak przydatne w języku wysokiego poziomu.

TheCatWhisperer
źródło
3
Mogą również zużywać mniej pamięci, jeśli struktury są wypełnione. Kilka dużych tablic, inteligentnie przydzielonych, może zużywać mniej pamięci niż tablica struktur.
Frank Hileman
4
4. Gdy części danych muszą być trzymane blisko siebie, aby efektywnie wykorzystać pamięć podręczną procesora. (Niezbędne w rzadkich przypadkach.)
Blrfl
@Frank Hileman, Whilie Myślę, że odpowiedź TheCatWhisperer jest całkowicie poprawna, twój komentarz jest właściwie najlepszym powodem, aby wybrać to podejście. Jeśli zużycie pamięci jest krytyczne, narzut pamięci na wypełnianie struktur może być znaczący, szczególnie jeśli w grę wchodzą duże liczby.
Vladimir Stokic
Dodałeś
Re (2), jak to jest? Mogę napisać program z jedną tablicą struktur i blokadą na pole tak samo łatwo, jak mogę napisać program z wieloma tablicami i blokadą na tablicę.
Solomon Slow
7

Byłem winny używania równoległych tablic . Czasami tak bardzo lubisz struktury, że nie chcesz myśleć o tym, jak ją wyodrębnić. Abstrakcji może być nieco trudniejsza do refaktoryzacji, więc nie masz ochoty jej uruchamiać, dopóki nie udowodnisz, czego naprawdę potrzebujesz.

W tym momencie warto zastanowić się nad refaktoryzacją w celu wyodrębnienia szczegółów. Często największym powodem, dla którego nie chcę tego robić, jest to, że trudno wymyślić dobre imię.

Jeśli widzisz dobry sposób na wyodrębnienie równoległych tablic, zrób to za każdym razem. Ale nie paraliżuj się, nie dotykając ich. Czasami trochę brudnego kodu jest najlepszym krokiem do dobrego kodu.

candied_orange
źródło
6

Ten wzorzec jest czasem nazywany również Strukturą tablic (w przeciwieństwie do Tablicy struktur) i jest niezwykle przydatny podczas wektoryzacji kodu. Zamiast pisać obliczenia, które działają na pojedynczej strukturze i wektoryzować jego bity, piszesz obliczenia w normalny sposób, z wyjątkiem wewnętrznych elementów SSE, tak aby działały na 4 strukturach zamiast jednej. Jest to zwykle łatwiejsze i prawie zawsze szybsze. Format SoA sprawia, że ​​jest to bardzo naturalne. Poprawia również wyrównanie, co przyspiesza operacje pamięci SSE.

Dan
źródło
Tak, to podejście jest stosowane podczas uczenia maszynowego na GPU. Zwyczajowo rozdziela się pola wielu oddzielnych przykładów, spakowuje wszystkie wartości każdego pola do osobnego tensora i przekazuje te tensory, aby je obliczyć zbiorczo, aby utworzyć listę prognoz.
Przywróć Monikę