Как масштабировать случайные симуляции с параллелизмом в Swift? - PullRequest
1 голос
/ 11 февраля 2020

Я играю в Swift 5.1 (на Ма c), проводя небольшие симуляции теннисных матчей. Естественно, часть моделирования случайным образом выбирает, кто выигрывает каждую точку.

Ниже приведена соответствующая часть кода, в которой я выполняю параллелизм.

func combine(result: MatchTally)
{
    overallTally.add(result: result)
}

DispatchQueue.concurrentPerform(iterations: cycleCount){iterationNumber in
    var counter = MatchTally()
    for _ in 1...numberOfSimulations
    {
        let result = playMatch(between: playerOne, and: playerTwo)
        counter[result.0, result.1] += 1
    }
    combiningQueue.sync {combine(result: counter)}
}

При выбранном соответствующем числе прогонов моделирования Одна очередь занимает около 5 секунд. Если я установил параллельные очереди равными 2, симуляция теперь занимает 3,8 с на очередь (т.е. потребовалось 7,2 с). Снова удвоение до 4 очередей приводит к 4,8 с / очередь. И, наконец, с 6 очередями (машина с 6-ядерным процессором Intel i7) все занимает 5,6 с / очередь.

Для тех, кому нужно больше убедить, что это связано с генерацией случайных чисел (я использую Double.random(0...1)) Я заменил код, в котором большинство случайных результатов генерируется с фиксированным результатом (я не мог заменить второе место, поскольку мне все еще нужно было на ie -брейке), и скорректировал количество симуляций соответствующим образом, результаты были следующими:

  • 1 очередь: 5 с / очередь
  • 2 очереди: 2,7 с / очередь
  • 4 очереди: 1,9 с / очередь
  • 6 очередей: 1.7 с / очередь

Итак, как вы можете видеть, кажется, что случайная часть устойчива к параллельной работе.

Я также пробовал с drand48 () и столкнулся с те же проблемы. Кто-нибудь знает, так ли это на самом деле?

Xcode 11.3, Swift 5.1, macOS 10.15.3, Ma c mini 2018, 6-ядерный i7 (но в разные годы сталкивался с одним и тем же аппаратное обеспечение)

Для тех, кто заинтересован в том, чтобы воспроизвести это сами, вот код, который я создал и добавил к Александру.

import Foundation

func formatTime(_ date: Date) -> String
{
    let df = DateFormatter()
    df.dateFormat = "h:mm:ss.SSS"
    return df.string(from: date)
}

func something(_ iteration: Int)
{
    var tally = 0.0
    let startTime = Date()
    print("Start #\(iteration) - \(formatTime(startTime))")

    for _ in 1...1_000_000
    {
        tally += Double.random(in: 0...100)
//      tally += 3.5
    }
    let endTime = Date()
    print("End   #\(iteration) - \(formatTime(endTime)) - elapsed: \(endTime.timeIntervalSince(startTime))")
}

print("Single task performed on main thread")
something(0)    //  Used to get a baseline for single run

print("\nMultiple tasks performed concurrently")
DispatchQueue.concurrentPerform(iterations: 5, execute: something)

Замена случайной добавки в l oop для фиксированного один демонстрирует, насколько хорошо код масштабируется в одном сценарии, но не другой.

1 Ответ

0 голосов
/ 14 февраля 2020

Похоже, решение состоит в том, чтобы использовать менее «модный» генератор, такой как drand48(). Я полагал, что уже тестировал этот вариант, но, похоже, я ошибся. Кажется, это не страдает от той же самой проблемы, поэтому я думаю, что это присуще arc4random(), на котором, я полагаю, основан Double.random().

Другой положительный момент заключается в том, что возвращение происходит примерно в 4 раза быстрее ценность. Так что мои симуляции не будут криптографически безопасными, но тогда какой же теннисный матч? ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...