Многоядерность стала стандартом. Даже в моем старом компьютере их четыре. Что действительно разочаровывает, так это то, что приложения по-прежнему не используют его должным образом. Я, конечно, не говорю о разногласиях или офисных продуктах. Использование параллелизма здесь на самом деле замедлило бы работу, но даже это не важно, поскольку им не нужна производительность во всем, что соответствует. Тем не менее, большие требования к производительности предъявляются к различным приложениям, таким как игры.

Игры действительно уникальные программы. Они должны сделать все как можно быстрее, чтобы предложить лучший опыт. Сначала стандартом было 30 кадров в секунду, затем он перешел на 60, а теперь медленно приближается к 120. Тем не менее, игры, явно ориентированные на огромное количество сущностей, по-прежнему работают в одном потоке. Хорошим примером этого является игра под названием Промышленность. Короче говоря, игра в жанре Tower Defense с песочницей, написанная на Java. Он действительно использует только одно ядро, если мы не принимаем во внимание Pathfinder. Если вы действительно хороши в игре и пережили достаточное количество волн, ваш компьютер начинает плакать от меньшего количества врагов и зданий. Но подождите секунду, загрузка ЦП составляет всего 25%, разве это не пустая трата времени?

Например, есть простой и эффективный способ заставить вашу игру использовать 100%, если это необходимо. Потребовалось некоторое время, чтобы понять, но «Система ворот» — это путь. Сначала, затем что-нибудь еще, мы должны четко указать, что не все должно быть сделано одновременно. Один естественный намек — как синхронизировать матчи, которые вы должны использовать, так как синхронизация всегда медленная. Излишне говорить, что отправка задач в основной поток может показаться хорошей идеей, я даже видел этот подход бесчисленное количество раз, но это также абсолютно не нужно. Выполнение нескольких разных задач одновременно — это дизайн кровати по своей сути!

Система ворот

Он вводит два состояния. Параллельные и непараллельные. Вещи, которые должны выполняться в одном потоке, будут выполняться во время неконкурентного, а все остальное — в параллельном. Есть 2 правила. Всегда выполняйте только один тип задач и, очевидно, запускайте только один поток или все сразу. И чтобы обеспечить применение второго правила, у вас есть ворота. Так называемые ворота можно представить как дверь в космический корабль. Он состоит из двух дверей, внутренней и внешней, их нельзя открыть одновременно, потому что из корабля вылетят все, включая членов экипажа. Теперь представьте, что сущности, которые хотят войти, — это потоки. Внешняя дверь откроется и пропустит всех в промежуточную зону. И поскольку все потоки ожидают, основной поток может выполнить то, что необходимо, затем закрыть внешнюю дверь и открыть внутреннюю дверь. Поскольку мы живем в мире циклов, наши потоки снова появятся у входной двери после того, как они выполнили все задачи. Затем, если мы хотим, чтобы потоки обрабатывали одни задачи, но не запускали другие, пока не будут завершены все остальные потоки, мы можем разместить больше вентилей, которые будут их синхронизировать.

Теперь, когда мы знаем, что такое система ворот, давайте поговорим о том, когда ее использовать, и о том, как лучше всего ее реализовать. Система Gate процветает, когда вам нужно выполнить одно и то же обновление для нескольких объектов, хранящихся в коллекции. Например, если у вас 4 ядра, ядро ​​может выполнять обновление для каждого четвертого объекта. Чтобы быть более конкретным, выполните обнаружение коллизий между первыми двумя воротами. Следующие ворота используют результаты вычислений для изменения всех объектов. Что, если вы знаете, где будут происходить столкновения, и вам просто нужно нанести урон? Пусть каждый поток собирает данные в свой собственный массив, а затем останавливает их все на воротах, а затем просто позволяет основному потоку выполнять нанесение повреждений или рендеринг. Последнее, что мы должны сделать, это поговорить о языке.

Голанг

Лучший способ показать себя в этом вопросе — просто показать код. Все, что вам нужно для шлюза, — это две структуры sync.Mutex и две структуры sync.WaitGroups. Во-первых, мы определяем дверь:

type Door struct {
    m sync.Mutex
    w sync.WaitGroup
    cap int // how match threads are we expecting to run
}
// Called from main thread, it will halt it until all threads arrive
func (d *Door) WaitFor() {
    d.w.Wait()
    d.w.Add(d.cap)
}
// Called from thread, sygnals thread arrival and waits on door    // until it opens
func (d *Door) WaitOn() {
    d.w.Done()
    d.m.Lock()
    d.m.Unlock()
}

Затем мы соединяем две двери вместе и определяем ворота.

type Gate struct {
    Inner, Outer Door
}
// Called from thread, makes thread wait on gate
func (g *Gate) Wait() {
    g.Outer.WaitOn()
    g.Inner.WaitOn()
}
// Called from main thread, lets all threads to inbetween area and // stops them. 
func (g *Gate) WaitForThreads() {
    g.Outer.WaitFor()
    g.Inner.m.Lock()
    g.Outer.m.Unlock()
}
// Called from main thread, lets all threads run tasks egan.
func (g *Gate) LetThreadsIn() {
    g.Inner.WaitFor()
    g.Outer.m.Lock()
    g.Inner.m.Unlock()
}

И вот, ваша игра теперь параллельна и будет использовать все доступные ресурсы, что также повышает частоту кадров.