- სტრუქტურების მასივს(Array of Structures).
- მასივების სტრუქტურას(Structure of Arrays).
- მასივების სტრუქტურების მასივი(Array of Structure of Arrays)
სურათი ვიზუალურად ასახავს AoS-დან SoA-ში მონაცემების ტრანსფორმაციას. |
//მონაცემთა სტრუქტურა რომელიც შედგება 4 float ელემენტისგან
struct color {
float r;
float g;
float b;
float a;
};
//მონაცემთა სტრუქტურა რომელიც ინახავს სტრუქტურების მასივს
struct pixelsAoS {
pixelsAoS(unsigned long int count) {
size = count;
rgba = new color[count];
}
~pixelsAoS() {
delete[] rgba;
}
color* rgba;
unsigned long int size;
};
ასეთ შემთხვევაში მონაცემები მეხსიერებაში განლაგდებიან შემდეგი თანმიმდევრობით: r1, g1, b1, a1, r2, g2, b2, a2, r3, g3, b3, a3, ..., rn, gn, bn, an. ახლა განვიხილოთ მასივების სტრუქტურის შემთხვევა. მოვიყვანოთ პროგრამული მაგალითი:
struct pixelsSoA {
pixelsSoA(unsigned long int count) {
size = count;
r = new float[count];
g = new float[count];
b = new float[count];
a = new float[count];
}
~pixelsSoA() {
delete[] r;
delete[] g;
delete[] b;
delete[] a;
}
float* r;
float* g;
float* b;
float* a;
unsigned long int size;
};
მასივების სტრუქტურის შემთხვევაში მონაცემები მეხსიერებაში გადალაგდებიან შემდეგნაირად: r1, r2, r3, r4, ...rn, g1, g2, g3, g4, ..., gn, b1, b2, b3, b4, ..., bn, a1, a2, a3, a4, ...an. მონაცემთა ჩაწერის ეს მეთოდი უფრო ეფექტურია კეშირების დროს, რადგან ასეთ ჩანაწერში ერთი და იგივე ველები მეხსიერებაში მიმდევრობით ხვდებიან და შესაბამისად კეშირებისას ნაკლებია იმის შანსი, რომ კეშში წამოღებულ მეხსიერების ნაწილს არასაჭირო ინფორმაცია წამოყვება. თუმცა ამ შემთხვევაში გასათვალისწინებელია კიდევ ერთი მნიშვნელოვანი რამ: რადგან ჩვენ პროცესორში გვაქვს ფიქსირებული ზომის კეში(რეალურად არსებობს რამოდენიმე დონის კეში სანამ ჩვენი მეხსიერება მიაღწევს პროცესორამდე), თუ ჩვენ გვექნება დიდი მასივები სტრუქტურაში და ისინი არ ჩაეტევიან კეშში, მაშინ კეშის მოქმედების ეფექტურობა ისევ დაეცემა. სწორედ ამიტომ გახდა მონაცემების კიდევ უფრო მოსახერხებელი ფორმით ჩაწერა რომელსაც ქვია მასივების სტრუქტურების მასივი. ასეთი ჩანაწერი საშუალებას გვაძლევს შევქმნათ მასივების სტრუქტურები რომელთაც ექნება სასურველი ზომა(ისეთი რომ კესში მთლიანად მოთავსდეს) და ამ ტიპის სტრუქტურის ელემენტები შევინახოთ მასივში. ასთი ტიპის ტრანსფორმაცია ზრდის არა მხოლოდ კეშის უშაობის ეფექტურობას არამედ ასევე მოსახერხებელია SIMD გამოთვლების დროს, რაც საბოლოო ჯამში ძალიან მკვეთრად მოქმედებს წარმადობაზე.
This comment has been removed by the author.
ReplyDeleteდავამატებ იმას რომ
ReplyDeletenew ოპერატორის გამოყენების შემთხვევაში მეხსიერების გამოყოფა ხდება RAM-ში (ოპერატიულში)
როგორც სტატიაშია აღნიშნული მასივის შემთხვევაში მეხსიერების ელემენტები ერთმანეთის მიმდევრობით არის დალაგებული, როცა გამოიყენება სტრუქტურების მასივი იმისათვის რომ მოხდეს მაგალითად
50-ე ელემენთის b ცვლადის მნიშვნელობის წაკითხვა ხდება შემდეგი ოპერაციები
პირველ რიგში გამოითვლება color სტრუქტურის ზომა (შეიძლება ჩაითვალოს რომ ეს კონსტანტა ამიტომ ამაზე დრო არც იხარჯება)
color სტრუქტურა შეიცავს 4 ცალ float ტიპის ცვლადს ხოლო float-ის ზომა არის 32 ბიტი ანუ (4 ბაიტი)
ანუ color სტრუქტურის ზომა გამოდის 32 * 4 = 128 ბიტი ხოლო ბაიტებში 4 * 4 = 16 ბაიტს
შესაბამისად 50-ე ელემენტის მისამართი იქნება 49 * 16 = 784 (49-ზე მრავლდება იმის გამო რომ ჩვენ გვინდა 50-ე ელემენტის დასაწყისი ანუ 49-ე ელემენტი ბოლოს მიმატებული 1 იქნება 50 ელემენტის პირველი ბაიტი) ანუ მასივის საწყისი მისამართიდან უნდა გადავთვალოთ 49 ცალი 16 ბაიტი და მივიღებთ 50-ე ელემენტის დასაწყისს ხოლო b ცვლადი color სტრუქტურაში არის 3-ე ცვლადი რაც ნიშნავს იმას რომ 50 ელემენტის დასაწყისიდან კიდევ უნდა გადავთვალოთ კიდებ 2 ცალი 32 ბიტი (ჯამში 64 ბიტი) ანუ 8 ბაიტი მივადგებით მესამე ცვლადის დასაწყისს... ამ ადგილიდან უნდა წავიკითხოთ წავიკითხოთ 32 ბიტი ანუ 4 ბაიტი და ეს იქნება 50-ე ელემენტის b ცვლადის მნიშვნელობა როგორც შეამჩნიეთ ხდება რამდენიმე გამოთვლითი ოპერაცია იმისათვის რომ 1 ცვლადის მნიშვნელობა ამოვიღოთ რაც მოიცავს მეხსიერებასთან რამოდენიმე მიმართვასაც (ცუდ შემთხვევაში ხოლო კარგ შემთხვევაში 50-ე ელემენთტს პროცესორი მთლიანად წაიღებს კეშში და შემდეგ წაიკითხავს მის 3-ე ცვლადის მნიშვნელობას) ასევე ამ ყველაფერს თუ დავუმათებთ მეხსიერებასთან მიმართვის ოპერაციებს რომელიც საკმაოდ ხანგრძლივი პროცესია (გსმენიათ ალბათ ოპერატიული მეხსირების Timing-ების შესახებ მაგალითად 5-5-5-15 ან 9-9-9-24... ეს ციფრები რეალურად არის პროცესორის ტაქტების რაოდენობა რომლის განმაბლობაშიც იგი ელოდება ოეპრატიულ მეხსიერებას რადგან მეხსიერება არ არის ისეთი სწრაფი როგორც პროცესორი... პირველ მაგალითში როგორც ხედავთ ჯამში არის 30 ტაქტი რომელიც იხარჯება მონაცემების წაკითვისას ოეპრატიულიდან)
უბრალოდ მასივების გამოყენების შემთხვევაში როგორც არის ნაჩვენები მეორე მაგალითში
პირდაპირ ხდება მისამათღების განსაზღვრა მაგალითად r მასივიდან 50-ე ელემენტის წაკითხვა არის ერთი მიმართვის ოპერაცია ანუ 4 მასივიდან მონაცემების წაკითხვა უდრის ოპერატიულზე 4 მიმართვის ოეპრაციას, ამიტომ დამოკიდებულია ოეპრაციების ტიპზე რომელიც უნდა ჩაატაროთ მონაცემებზე და ამის მიხედვით უნდა განსაზღვროთ თუ რომელი სტრუქტურით გირჩევნიათ მონაცემების აღწერა მეხსიერებაში (მასივების სახით თუ სტრუქტურების მასივის სახით)
რადგან როცა პროცესორი ხედავ რომ ინსტრუქციაში ფიგურირებს არა მნიშვნელობა არამედ მეხსირების მისამართი საიდანაც უნდა მოხდეს მნიშვნელობის წაკითხვა იგი პირველ რიგში ცდილობს ამ მნიშვნელობის L2 კეშში პოვნას თუ ვერ იპოვა მიდის ოპერატიულში და იქიდან მოაქვს ეს მნიშვნელობები და ინახავს L2 კეშში და შემდეგ იყენებს მათ... ამიტომ არის L2 კეში ყოველთვის დიდი ზომის რადგან მეორედ დონის ეშ მეხსიერებაში მონაცემები ყოველთვის მოდის ოეპრატიულიდან Bulk კოპირების სახით რომ წინასწარ ჰქონდეს ის მონაცემები რომლების შეიძლება დაჭირდეს პროცესორს... ამ თემასთან დაკავშირებით საკმაოდ კარგი და ვცელი სტატიები დევს wasm.ru-ზე და ეს საკითხის და ზოგადად პროცესორების მუშაობის პრინციპი გარჩეულია სხვადასხვა მწარმოებლების სხვადასხვა პროცესორების დონეზე და არა ზოგადი კონცეპციის სახით ;)
მშვენიერია ვაჟა, კარგია რომ გესმის თემა. დიდი და ვრცელი სტატიის დაწერას ყოველთვის ვერიდები, რადგან აბნევს მკითხველს. პირველი ეტაპისთვის ზოგადი სახით მიმოვიხილე რაშია მთავარი იდეა ჩადებული და საერთოდ რომ უნდა მიექცეს ასეთ რამეს ყურადღება პროგრამირებისას. ხშირ შემთხვევაში SoA-ს სახით აღწერა კოდის კითხვადობას ამცირებს და დიზაინს არღვევს, ამიტომ თუ კრიტიკული არაა მოთხოვნა მაქსიმალურ სისწრაფეზე თავს არიდებენ ხოლმე გამოყენებას. :)
ReplyDelete