Для проекта исследования я написал приложение Scala, которое использует кучу фьючерсов для параллельного вычисления. Я заметил, что на моей локальной машине (4 ядра) код работает быстрее, чем на многоядерном сервере нашего института информатики (64 ядра). Теперь я хочу знать, почему это так.
Задача в деталях
Задача состояла в том, чтобы создать произвольные логические формулы k-CNF с n различными переменными, случайным образом распределенными по m-предложениям, а затем посмотреть, как с помощью комбинации m/n вероятность того, что формула разрешима, падает на 50% для разных случайных распределений. Для этого я применил вероятностный алгоритм k-SAT, генератор предложения и некоторый другой код. Ядро - это функция, которая принимает n и m хорошо, как функция генератора, выполняет 100 фьючерсов и ждет результата. Функция выглядит следующим образом:Код, о котором идет речь
def avgNonvalidClauses(n: Int, m: Int)(implicit clauseGenerator: ClauseGenerator) = {
val startTime = System.nanoTime
/** how man iteration to build the average **/
val TRIES = 100
// do TRIES iterations in parallel
val tasks = for (i <- 0 until TRIES) yield future[Option[Config]] {
val clause = clauseGenerator(m, n)
val solution = CNFSolver.probKSat(clause)
solution
}
/* wait for all threads to finish and collect the results. we will only wait
* at most TRIES * 100ms (note: flatten filters out all
* None's) */
val results = awaitAll(100 * TRIES, tasks: _*).asInstanceOf[List[Option[Option[Config]]]].flatten
val millis = Duration(System.nanoTime - startTime, NANOSECONDS).toMillis
val avg = (results count (_.isDefined)) / results.length.toFloat
println(s"n=$n, m=$m => $avg ($millis ms)")
avg
}
Проблема
На моей локальной машине я получаю эти результаты
[info] Running Main
n=20, m=120 => 0.0 (8885 ms)
n=21, m=121 => 0.0 (9115 ms)
n=22, m=122 => 0.0 (8724 ms)
n=23, m=123 => 0.0 (8433 ms)
n=24, m=124 => 0.0 (8544 ms)
n=25, m=125 => 0.0 (8858 ms)
[success] Total time: 53 s, completed Jan 9, 2013 8:21:30 PM
На 64-ядерном сервере я получаю:
[info] Running Main
n=20, m=120 => 0.0 (43200 ms)
n=21, m=121 => 0.0 (38826 ms)
n=22, m=122 => 0.0 (38728 ms)
n=23, m=123 => 0.0 (32737 ms)
n=24, m=124 => 0.0 (41196 ms)
n=25, m=125 => 0.0 (42323 ms)
[success] Total time: 245 s, completed 09.01.2013 20:28:22
Тем не менее, я полностью загружаю обе машины (средний сервер на нагрузке от 60 до 65), поэтому есть достаточное количество потоков. Почему это? Я делаю что-то совершенно неправильное?
На моей локальной машине установлен процессор AMD Phenom (tm) II X4 955 ", на котором используется процессор AMD Opteron (TM) 6272". Локальный процессор имеет 6800 богомипсов, серверов 4200. Таким образом, хотя локальный процессор на 1/3 быстрее, на сервере в 12 раз больше.
Дополнительные
Если у меня есть урезанный пример моего кода, толкаемого в github, чтобы вы могли попробовать его, если вы заинтересованы: https://github.com/Blattlaus/algodemo (It проект sbt с использованием Scala 2.10).
Обновление
- Я уничтожил любую случайность, затравив генераторы случайных чисел на 42. Это ничего не меняет.
- Я изменил набор тестов. Теперь результаты еще более изумительны (сервер в 5 раз медленнее!) Примечание: все выходы для среднего процента не разрешимых предложений - это из-за ввода. Это нормально и ожидается.
- Добавлена информация о процессорах
- Я заметил, что вызовы Random.nextInt() на 10 раз меньше, чем на сервере. Я завернул все вызовы в помощнике, который измеряет время выполнения отпечатков на консоли, если они медленнее, чем 10 мс. На моей локальной машине я получаю несколько, и обычно это паузы 10-20 мс. На сервере я получаю много звонков с мурами, и они, как правило, выше 100 мс. Может ли это быть проблемой?