Багатопотокові розрахунки гравітації


24

Я будую гру з дослідження космосу, і в даний час я почав працювати над гравітацією (в C # з XNA).

Сила тяжіння все ще потребує налаштування, але перш ніж я можу це зробити, мені потрібно вирішити деякі проблеми з моїми фізичними розрахунками.

Для цього використовується 100 об'єктів, звичайно рендерінг 1000 з них без фізичних обчислень отримує понад 300 FPS (що є моєю кришкою FPS), але будь-які об'єкти більше 10 або більше приносять гру (та одну нитку, на якій вона працює) до своєї коліна при виконанні фізичних розрахунків.

Я перевірив моє використання потоку, і перший потік вбивав себе від усієї роботи, тому я зрозумів, що мені просто потрібно зробити розрахунок фізики на іншій нитці. Однак, коли я намагаюся запустити метод оновлення класу Gravity.cs на інший потік, навіть якщо в методі оновлення Gravity немає нічого, гра все ще знижується до 2 FPS.

Гравітація.cs

public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in entityEngine.Entities)
        {
            Vector2 Force = new Vector2();

            foreach (KeyValuePair<string, Entity> e2 in entityEngine.Entities)
            {
                if (e2.Key != e.Key)
                {
                    float distance = Vector2.Distance(entityEngine.Entities[e.Key].Position, entityEngine.Entities[e2.Key].Position);
                    if (distance > (entityEngine.Entities[e.Key].Texture.Width / 2 + entityEngine.Entities[e2.Key].Texture.Width / 2))
                    {
                        double angle = Math.Atan2(entityEngine.Entities[e2.Key].Position.Y - entityEngine.Entities[e.Key].Position.Y, entityEngine.Entities[e2.Key].Position.X - entityEngine.Entities[e.Key].Position.X);

                        float mult = 0.1f *
                            (entityEngine.Entities[e.Key].Mass * entityEngine.Entities[e2.Key].Mass) / distance * distance;

                        Vector2 VecForce = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
                        VecForce.Normalize();

                        Force = Vector2.Add(Force, VecForce * mult);
                    }
                }
            }

            entityEngine.Entities[e.Key].Position += Force;
        }

    }

Так, я знаю. Це вкладений цикл foreach, але я не знаю, як ще зробити підрахунок сили тяжіння, і це, здається, працює, воно настільки інтенсивне, що йому потрібна власна нитка. (Навіть якщо хтось знає надзвичайно ефективний спосіб зробити ці обчислення, я все одно хотів би знати, як я можу це зробити на кількох потоках)

EntityEngine.cs (керує екземпляром Gravity.cs)

public class EntityEngine
{
    public Dictionary<string, Entity> Entities = new Dictionary<string, Entity>();
    public Gravity gravity;
    private Thread T;


    public EntityEngine()
    {
        gravity = new Gravity(this);
    }


    public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in Entities)
        {
            Entities[e.Key].Update();
        }

        T = new Thread(new ThreadStart(gravity.Update));
        T.IsBackground = true;
        T.Start();
    }

}

EntityEngine створюється в Game1.cs, а його метод Update () викликається в Game1.cs.

Мені потрібен мій розрахунок фізики в Gravity.cs для запуску кожного разу, коли гра оновлюється, окремим потоком, щоб розрахунок не сповільнив гру до жахливо низького (0-2) FPS.

Як би я пішов про те, щоб зробити цю роботу з ниткою? (будь-які пропозиції щодо вдосконаленої системи планетарної тяжкості вітаються, якщо хтось має)

Я також не шукаю уроку, чому я не повинен використовувати різьблення чи небезпеку неправильного використання, шукаю пряму відповідь, як це зробити. Я вже витратив годину на гуглінг цього самого питання з невеликими результатами, які я зрозумів або були корисними. Я не маю на увазі грубість, але це завжди здається важким, як програмування noob отримати прямий змістовний відповідь, я, як правило, швидше отримую відповідь настільки складний, я легко міг би вирішити свою проблему, якби я зрозумів це, або хтось сказав, чому я не повинен робити те, що хочу зробити, і не пропоную альтернатив (які корисні).

Дякую за допомогу!

EDIT : Прочитавши відповіді, які я отримав, я бачу, що ви, хлопці, насправді хвилюєтесь і не просто намагаєтесь вияснити відповідь, яка може спрацювати. Мені хотілося вбити двох птахів одним каменем (покращуючи продуктивність та вивчаючи деякі основи багатопотокового прочитання), але, здається, більша частина проблеми лежить у моїх розрахунках, і те, що нарізання рішень - це більше клопоту, ніж варто для підвищення продуктивності. Дякую всім, я ще раз прочитаю ваші відповіді та спробую ваші рішення, коли закінчу зі школою, ще раз дякую!


Що робить це [ваша система потокового оновлення, описана вище] зараз (чи працює)? До речі, я запустив би це якнайшвидше в ігровому циклі - наприклад, до того, як сутність буде оновлена.
ThorinII

2
Trig-дзвінки в інтер'єрі вкладених петель, мабуть, є найбільшим хітом. Якщо ви можете знайти спосіб , щоб усунути їх, що буде зменшувати kцієї O(n^2)проблеми багато.
RBarryYoung

1
Дійсно, виклики тригерів абсолютно непотрібні : ви спочатку обчислюєте кут від вектора, а потім використовуєте його для генерації іншого вектора, який вказує у заданому напрямку. Тоді ви нормалізуєте цей вектор, але оскільки sin² + cos² ≡ 1він все одно нормалізується! Ви могли просто використати оригінальний вектор, який з'єднує два об'єкти, які вас цікавлять, і нормалізував цей. Жодного триггера не вимагає.
близько

Чи не застаріла XNA?
jcora

@yannbane це запитання не додає нічого корисного для обговорення. І ні, статус XNA не відповідає жодному визначенню застарілого.
Сет Беттін

Відповіді:


36

Тут у вас є класичний алгоритм O (n²) . Першопричина вашої проблеми не має нічого спільного з нарізкою різьби, і все, що стосується того, що ваш алгоритм має високу складність.

Якщо ви раніше не стикалися з позначеннями "Big O", це в основному означає кількість операцій, необхідних для роботи над n елементами (це надпросте пояснення). Ваші 100 елементів виконують внутрішню частину петлі 10000 разів .

У розробці ігор, як правило, ви хочете уникати алгоритмів O (n²) , якщо у вас є невеликий (а краще фіксований або обмежений) обсяг даних і дуже швидкий алгоритм.

Якби кожна сутність впливала на будь-яку іншу сутність, вам за необхідності потрібен був би алгоритм O (n²). Але схоже, що лише кілька об'єктів насправді взаємодіють (завдяки if (distance < ...)), тому ви могли значно зменшити кількість своїх операцій, використовуючи щось, що називається " Просторовий розподіл ".

Оскільки це досить детальна тема і дещо специфічна для гри, я рекомендую вам задати нове запитання для отримання більш детальної інформації. Перейдемо далі ...


Одна з основних проблем продуктивності вашого коду - досить проста. Це страшно повільно :

foreach (KeyValuePair<string, Entity> e in Entities)
{
    Entities[e.Key].Update();
}

Ви робите пошук словника за рядком, кожну ітерацію (кілька разів у інших циклах) для об'єкта, який у вас уже є!

Ви можете зробити це:

foreach (KeyValuePair<string, Entity> e in Entities)
{
    e.Value.Update();
}

Або ви могли б зробити це: (особисто мені це подобається краще, обидва повинні бути приблизно з однаковою швидкістю)

foreach (Entity e in Entities.Values)
{
    e.Update();
}

Пошук словника за рядком відбувається досить повільно. Ітерація безпосередньо буде значно швидшою.

Хоча, як часто вам насправді потрібно шукати предмети по імені? Порівняно з тим, як часто ви повинні повторювати їх усі? Якщо ви шукаєте лише імена рідко, подумайте про збереження ваших об'єктів у List(дайте їм свого Nameчлена).

Код, який у вас є, є відносно тривіальним. Я цього не зробив, але я вважаю, що більша частина часу на виконання буде спрямована на повторні пошукові словники . Ваш код може бути "досить швидким", лише виправивши цю проблему.

EDIT: Наступна найбільша проблема - це, ймовірно, дзвінок, Atan2а потім негайно перетворення його назад у вектор з Sinі Cos! Просто використовуйте вектор безпосередньо.


Нарешті, давайте розглянемо нарізку теми та основні проблеми у вашому коді:

Перше і найочевидніше: не створюйте нову нитку для кожного кадру! Предмети з нитки досить "важкі". Найпростішим рішенням цього було б просто використовувати ThreadPoolзамість цього.

Звичайно, це не так просто. Перейдемо до проблеми номер два: не торкайтеся даних по двох потоках одразу! (Без додавання відповідної інфраструктури безпеки потоку.)

Ви в основному тупотить пам'яті тут в самому жахливому шляху. Тут немає жодної безпеки. Будь-який з декількох gravity.Updateпотоків, які ви " " починаєте, може перезаписувати дані, що використовуються в інший потік у несподівані часи. Тим часом, ваша головна нитка, без сумніву, стосуватиметься всіх цих структур даних. Я не був би здивований, якби цей код спричинив важкі для відтворення порушення доступу до пам'яті.

Зробити щось подібне до цієї теми безпечним і важко, і це може призвести до значної продуктивності , що часто не варто витрачати зусиль.


Але, побачивши, як ви просили (не дуже) приємно про те, як це зробити все одно, давайте поговоримо про це ...

Як правило, я б рекомендував починати, практикуючи щось просте, де ваша нитка в основному є "вогнем і забути". Відтворення аудіо, запис щось на диск і т. Д. Все ускладнюється, коли потрібно повернути результат в основну нитку.

В основному існує три підходи до вашої проблеми:

1) Поставте блокування навколо всіх даних, які ви використовуєте в потоках. У C # це робиться досить просто з lockтвердженням.

Як правило, ви створюєте (і зберігаєте!) new objectСпеціально для блокування для захисту деякого набору даних (саме з міркувань безпеки зазвичай з'являються лише під час написання загальнодоступних API-інтерфейсів, але все-таки хороший стиль). Потім ви повинні заблокувати ваш об'єкт блокування скрізь, коли ви отримуєте доступ до даних, які він захищає!

Звичайно, якщо щось "заблоковано" одним потоком, оскільки воно використовується, а інший потік намагається отримати доступ до нього - той другий потік буде змушений чекати, поки перша нитка не закінчиться. Тож якщо ви ретельно не будете вибирати завдання, які можна виконувати паралельно, ви, як правило, отримуватимете однопотокове виконання (або гірше).

Тож у вашому випадку це не має сенсу робити, якщо ви не зможете сконструювати свою гру так, щоб паралельно працював якийсь інший код, який не торкнеться вашої колекції сутності.

2) Скопіюйте дані в потік, нехай обробляється, а потім знову вийміть результат, коли закінчите.

Від того, як ви це реалізуєте, залежатиме, що ви робите. Але очевидно, що це передбачає потенційно дорогу операцію копіювання (або дві), яка у багатьох випадках буде повільнішою, ніж просто виконувати речі з одноточковими.

І, звичайно, вам все ще належить виконати якусь іншу роботу на задньому плані, інакше ваша основна нитка просто буде сидіти навколо, чекаючи, коли інша нитка закінчиться, щоб вона могла скопіювати дані назад!

3) Використовуйте безпечні для потоків структури даних.

Вони досить повільніші, ніж їх однопоточні аналоги, і часто складніше у використанні, ніж просте блокування. Вони все ще можуть мати проблеми з блокуванням (зменшення продуктивності до однієї нитки), якщо ви не використовуєте їх обережно.


Нарешті, оскільки це моделювання на основі кадру, вам потрібно буде мати головний потік зачекати, щоб інші потоки забезпечили свої результати, щоб цей кадр міг бути рендерований і моделювання може продовжуватися. Повне пояснення дійсно занадто довго, щоб розмістити тут, але в основному ви захочете навчитися користуватися Monitor.Waitта Monitor.Pulse. Ось стаття для початку .


Я знаю, що я не дав конкретних деталей щодо реалізації (крім останнього біта) або коду для жодного з цих підходів. Перш за все, було б багато чого висвітлити. По-друге, жоден з них не застосовується до коду самостійно - вам потрібно підійти до всієї архітектури з поглядом на додавання потоків.

Тематика не магічно зробить код, який у вас там швидше - він дозволяє одночасно робити щось інше!


8
+10, якби міг. Можливо, ви можете перенести останнє речення вгору як вступ, оскільки воно узагальнює тут основну проблему. Запуск коду на іншій нитці не магічно прискорює візуалізацію, якщо у вас немає нічого іншого робити одночасно. І візуалізатор, ймовірно, чекає, коли нитка закінчиться, але якщо вона цього не зробить (і як він міг це знати?), Він буде малювати невідповідний стан гри з фізикою сутності, яка все ще має бути оновлена.
LearnCocos2D

Я повністю переконаний, що введення ниток - це не те, що мені потрібно, дякую за тривалу та знаючу інформацію! Що стосується покращення продуктивності, я вніс запропоновані вами зміни (та інші), але я все ще отримую погану ефективність, маючи справу з> 60 об’єктами. Я думаю, що було б найкраще зробити ще одне питання, орієнтований на ефективність моделювання N-Body. Ви все-таки отримаєте мою відповідь. Спасибі!
Листоноша

1
Запрошуємо вас, радий, що це допомогло :) Коли ви опублікуєте своє свіже запитання, будь ласка, опустіть посилання сюди, щоб я та інші, хто слідував за ним, побачили його.
Ендрю Рассел

@Postman Хоча я згоден з тим, що ця відповідь говорить в цілому, я думаю, що це повністю пропускає той факт, що це, в основному, алгоритм PERFECT, щоб скористатися перевагою нитки. Існує причина, що вони роблять це на графічному процесорі, і це тому, що це тривіально паралельний алгоритм, якщо ви перемістите запис на другий крок. Немає необхідності в блокуванні, копіюванні та захищенні потоком даних даних. Простий Parallel.ForEach і робиться без проблем.
Chewy Gumball

@ChewyGumball Дуже вірний момент! І хоча Поштальон повинен був зробити свій алгоритм двофазним, він, мабуть, повинен бути двофазним. Варто зазначити, що Parallelце не обійшлося без накладних витрат, тому це, безумовно, щось для профілактики - особливо для таких невеликих наборів даних та (що має бути) відносно швидкого фрагмента коду. І, звичайно, все-таки краще зменшити складність алгоритму в цьому випадку - а не просто кидати на нього паралелізм.
Ендрю Рассел

22

Добре на перший погляд є кілька речей, які слід спробувати. Спочатку слід спробувати зменшити перевірку зіткнення, ви можете це зробити, використовуючи якусь просторову структуру, наприклад, a квадратик . Це дозволить вам зменшити кількість другого передбачення, оскільки ви будете лише запитувати об'єкти, які закривають перший.

Щодо вашої нитки: Намагайтеся не створювати потік кожного кроку оновлення. Цей наклад може, можливо, уповільнити ваше більше, ніж його прискорення. Натомість спробуйте створити єдину нитку зіткнення і нехай це зробить роботу за вас. У мене немає конкретного підходу copy-paste-this-code , але є статті про синхронізацію потоків та фоновий працівник для C #.

Ще один момент полягає в тому, що в циклі foreach вам не потрібно робити, entityEngine.Entities[e.Key].Textureтому що ви вже отримали доступ до диктату в заголовку foreach. Замість цього ви можете просто написати e.Texture. Я насправді не знаю про вплив цього, просто хотів вас повідомити;)

І останнє: На даний момент ви двічі перевіряєте кожну сутність, оскільки вона отримує запит у першому І другому циклі передбачення.

Приклад з 2 сутностями A і B:

pick A in first foreach loop
   pick A in second foreach loop
      skip A because keys are the same
   pick B in second foreach loop
      collision stuff
pick B in first foreach loop
   pick A in second foreach loop
      collision stuff
   pick B in second foreach loop
      skip B because keys are the same

Хоча це можливий підхід, можливо, ви можете впоратися з A і B за один виток, пропустивши половину ваших перевірок на зіткнення

Сподіваюся, що з цього ви почнете =)

PS: Навіть якщо ви сказали, що не хочете його чути: Спробуйте зберегти виявлення зіткнення в тій же нитці і просто достатньо прискорити його. Тематика здається гарною ідеєю, але з цим виникає потреба синхронізуватися як пекло. Якщо перевірка на зіткнення проходить повільніше, ніж ваше оновлення (причина його нанизування), ви отримаєте збої та помилки, тому що зіткнення запуститься після того, як кораблі вже рухаються, і навпаки. Я не хочу перешкоджати вам, це лише особистий досвід.

EDIT1: Посилання з QuadTree підручником (Java): http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/


10
Приємне використання квадрококтейлів для моделювання гравітації полягає в тому, що замість того, щоб просто ігнорувати віддалені частинки, ви можете зберігати загальну масу та центр маси всіх частинок у кожній гілці вашого дерева і використовувати це для обчислення середнього гравітаційного ефекту всіх частинок цієї гілки на інших, віддалених частинках. Це відоме як алгоритм Барнса-Хата , і саме це використовують профі .
Ільмарі Каронен

10

Чесно кажучи, перший що вам слід зробити, це перейти на кращий алгоритм.

Паралелізація моделювання може, навіть у найкращому можливому випадку, прискорити його лише на коефіцієнт, рівний кількості CPU × ядер на CPU × ниток на ядро, наявних у вашій системі - тобто десь від 4 до 16 для сучасного ПК. (Переміщення коду до GPU може принести багато вражаючі фактори паралелізації за рахунок додаткової складності розробки та меншої швидкості обчислення базової лінії за ниткою.) За допомогою алгоритму O (n²), як у вашому прикладі коду, це дозволить вам використовуйте від 2 до 4 разів більше частинок, ніж у вас є.

І навпаки, перехід на більш ефективний алгоритм може легко пришвидшити ваше моделювання, скажімо, коефіцієнтом від 100 до 10000 (цифри суто здогадані). Часова складність хороших алгоритмів моделювання n-тіла, використовуючи масштаби просторового підрозділу приблизно як O (n log n), що є "майже лінійним", так що можна було очікувати майже такого ж коефіцієнта збільшення кількості частинок, з якими можна обробити. Крім того , що буде по- , як і раніше використовувати тільки один потік, так що все ще буде місце для розпаралелювання на вершині , що .

У будь-якому випадку, як відзначали інші відповіді, загальним прийомом ефективного моделювання великої кількості взаємодіючих частинок є організація їх у квадраті (у 2D) або в octree (у 3D). Зокрема, для моделювання сили тяжіння основним алгоритмом, який ви хочете використовувати, є алгоритм імітації Барнса – Хата , в якому ви зберігаєте загальну масу (і центр маси) усіх частинок, що містяться в кожній комірці вашої квадратики / октриси та використовуйте це для наближення середнього гравітаційного впливу частинок у цій клітині на інші, віддалені частинки.

Ви можете знайти безліч описів та навчальних посібників за алгоритмом Барнса-Хата від Googling для нього, але ось приємний і простий для початку , а ось опис вдосконаленої реалізації, що використовується для моделювання GPU зіткнень галактики.


6

Ще одна відповідь на оптимізацію, яка не має нічого спільного з потоками. Вибач за це.

Ви обчислюєте відстань () кожної пари. Це передбачає взяти квадратний корінь, який відбувається повільно. Він також включає декілька пошукових об'єктів для отримання фактичних розмірів.

Ви можете оптимізувати це, скориставшись функцією DistanceSquared (). Заздалегідь підрахуйте максимальну відстань, на якій можуть взаємодіяти будь-які два об’єкти, покладіть їх на квадрат, а потім порівняйте це з DistanceSquared (). Якщо і тільки якщо відстань у квадраті знаходиться в межах максимуму, то візьміть квадратний корінь і порівняйте його з реальними розмірами об'єкта.

EDIT : Ця оптимізація в основному призначена для тестування на зіткнення, що я зараз помітив, це насправді не те, що ви робите (хоча ви, безумовно, в якийсь момент). Це все ще може бути застосовно до вашої ситуації, якщо всі частинки мають однаковий розмір / масу.


Так. Це рішення може бути нормальним (лише незначна втрата точності), але потрапляє в проблеми, коли маса предметів сильно відрізняється. Якщо маса деяких об'єктів дуже величезна, тоді як маса деяких об'єктів дуже мала, максимальна відстань для розумних вище. Наприклад, вплив земної сили на малу пилову частинку є незначним для землі, але не для пилової частинки (на досить великій відстані). Але насправді дві частинки пилу на одній відстані не впливають істотно одна на одну.
SDwarfs

Насправді це дуже вдалий момент. Я неправильно читаю це як тест на зіткнення, але насправді це робить навпаки: частинки впливають один на одного, якщо вони не торкаються.
Алістер Бакстон

3

Я не знаю багато про нитку, але здається, що ваші петлі забирають багато часу, тому, можливо, це змінюється

i = 0; i < count; i++
  j = 0; j < count; j++

  object_i += force(object_j);

до цього

i = 0; i < count-1; i++
  j = i+1; j < count; j++

  object_i += force(object_j);
  object_j += force(object_i);

може допомогти


1
чому б це допомогло?

1
Оскільки перші дві петлі роблять 10 000 ітерацій, а другі - лише 4 950 ітерацій.
Buksy

1

Якщо у вас вже є такі величезні проблеми з 10 імітованими об’єктами, вам потрібно буде оптимізувати код! Ваш вкладений цикл спричинив би лише 10 * 10 ітерацій, з яких 10 ітерацій пропускаються (той самий об'єкт), в результаті чого 90 ітерацій внутрішньої петлі. Якщо ви досягнете лише 2 FPS, це означатиме, що ваша продуктивність настільки погана, що ви досягаєте лише 180 ітерацій внутрішньої петлі за секунду.

Я пропоную вам зробити наступне:

  1. ПІДГОТОВКА / БЕНХМАРКІНГ: Щоб точно знати, що ця рутина є проблемою, напишіть невелику процедуру орієнтиру. Він повинен виконати Update()метод Гравітації кілька разів, наприклад, 1000 разів і виміряти час. Якщо ви хочете досягти 30 FPS зі 100 об'єктами, вам слід імітувати 100 об'єктів і виміряти час на 30 виконання. Це має бути менше 1 секунди. Використання такого еталону потрібно для розумних оптимізацій. Інакше ви, мабуть, досягнете протилежного і змусите код працювати повільніше, тому що ви просто думаєте, що він повинен бути швидшим ... Тож я дуже закликаю вас до цього!

  2. ОПТИМІЗАЦІЇ: Хоча ви не можете багато зробити з проблемою зусиль O (N²) (мається на увазі: час обчислення збільшується квадратично з кількістю модельованих об'єктів N), ви можете вдосконалити сам код.

    a) Ви використовуєте багато "асоціативного масиву" (Словник) пошуку у вашому коді. Це повільно! Наприклад entityEngine.Entities[e.Key].Position. Ви не можете просто використовувати e.Value.Position? Це економить один пошук. Ви робите це скрізь у цілому внутрішньому циклі, щоб отримати доступ до властивостей об'єктів, на які посилаються e і e2 ... Змініть це! б) Ви створюєте новий вектор всередині циклу new Vector2( .... ). Усі "нові" дзвінки означають деякий розподіл пам'яті (а пізніше: розселення). Вони навіть набагато повільніше, ніж пошукові словники. Якщо цей вектор вам потрібен лише тимчасово, то виділіть його поза петлями AND -reuse- він повторно реалізує свої значення до нових значень замість створення нового об’єкта. в) Ви використовуєте багато тригонометричних функцій (наприклад, atan2іcos) всередині циклу. Якщо для вашої точності не потрібно дійсно точно, ви можете спробувати скористатися таблицею пошуку. Для цього ви масштабуєте своє значення до визначеного діапазону, округляйте його до цілого значення та шукаєте його в таблиці заздалегідь обчислених результатів. Якщо вам потрібна допомога з цим, просто запитайте. г) Ви часто використовуєте .Texture.Width / 2. Ви можете попередньо обчислити це і зберегти результат як .Texture.HalfWidthабо, якщо це завжди навіть додатне ціле значення - ви можете використовувати біт операції зсуву,>> 1 розділити на два.

Зробіть лише одну із змін за один раз і виміряйте зміни за еталоном, щоб побачити, як це вплинуло на час виконання! Можливо, одна справа хороша, а інша - погана (навіть я їх пропонував вище!) ...

Я думаю, що ці оптимізації будуть набагато кращими, ніж намагатися досягти кращої продуктивності за допомогою декількох потоків! У вас буде багато проблем з координацією потоків, тому вони не перезаписують інші значення. Також вони конфліктуватимуть при доступі до подібних областей пам'яті. Якщо ви використовуєте для цього завдання 4 процесора / нитки, ви можете очікувати лише швидкості кадрів від 2 до 3 для частоти кадрів.


0

Чи можете ви це переробити без ліній створення об'єктів?

Vector2 Force = новий Vector2 ();

Vector2 VecForce = новий Vector2 ((float) Math.Cos (кут), (float) Math.Sin (кут));

якщо, можливо, ви могли б розмістити значення сили в об'єкті, а не створювати два нових об'єкти кожен раз, це може допомогти підвищити продуктивність.


4
Vector2у XNA - тип значення . У нього немає накладних витрат на GC, а накладні конструкції незначні. Це не є джерелом проблеми.
Ендрю Рассел

@Andrew Russell: Я не впевнений, але це все-таки так, якщо ви використовуєте "new Vector2"? Якщо ви використовуєте Vector2 (....) без "нового", це, мабуть, інакше.
SDwarfs

1
@StefanK. У C # ви не можете цього зробити. Потрібно нове. Ви думаєте про C ++?
MrKWatkins
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.