Это дополнительный вопрос к вопросу, который я задавал ранее, но я думаю, что мне следует начать все сначала. Я пытаюсь реализовать симуляцию числа Пи Монте-Карло и использую numba для повышения производительности. Поскольку каждая итерация цикла независима от других, я подумал, что смогу повысить производительность с помощью Parallel=True и numba.prange. Я попробовал и понял, что для небольших значений n распараллеливание того не стоит. Я попробовал улучшенную версию, в которой я использую распараллеливание после пересечения определенного порога для n, но обнаружил, что большую часть времени она работает хуже, чем мои предыдущие попытки. Теперь у меня есть сжатие трех версий алгоритма: обычная без распараллеливания, параллельная версия с использованием numba.prange и «улучшенная» гибридная версия, использующая распараллеливание после того, как указанный порог для n равен перечеркнуто:
from datetime import timedelta
from time import perf_counter
import numba as nb
import numpy as np
import numpy.typing as npt
if jit_opts.get("cache", False):
print("Using cached JIT compilation")
else:
print("Using JIT compilation without caching")
print()
print("Using parallel count_points_in_circle")
for i, n in enumerate(n_values):
start = perf_counter()
points, in_circle, pi_approx = monte_carlo_pi_parallel(n)
end = perf_counter()
duration = end - start
time_results[i, 0] = duration
delta = timedelta(seconds=duration)
elapsed_msg = (
f"[{delta} (Raw time: {duration} s)]"
if delta
else f"[Raw time: {duration} s]"
)
print(
f"n = {n:,}:".ljust(20),
f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20),
elapsed_msg,
)
print()
print("Using non-parallel count_points_in_circle")
for i, n in enumerate(n_values):
start = perf_counter()
points, in_circle, pi_approx = monte_carlo_pi(n)
end = perf_counter()
duration = end - start
delta = timedelta(seconds=duration)
time_results[i, 1] = duration
elapsed_msg = (
f"[{delta} (Raw time: {duration} s)]"
if delta
else f"[Raw time: {duration} s]"
)
print(
f"n = {n:,}:".ljust(20),
f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20),
elapsed_msg,
)
print()
print("Improved version:")
for i, n in enumerate(n_values):
start = perf_counter()
points, in_circle, pi_approx = monte_carlo_pi_improved(n)
end = perf_counter()
duration = end - start
delta = timedelta(seconds=duration)
time_results[i, 2] = duration
elapsed_msg = (
f"[{delta} (Raw time: {duration} s)]"
if delta
else f"[Raw time: {duration} s]"
)
print(
f"n = {n:,}:".ljust(20),
f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20),
elapsed_msg,
)
print()
print("Comparison:")
result_types = ("parallel", "non-parallel", "improved")
for n, res in zip(n_values, time_results):
res_idx = np.argsort(res)
print(
f"n = {n:,}:".ljust(20),
f"{result_types[res_idx[0]]} \N{LESS-THAN OR EQUAL TO} "
f"{result_types[res_idx[1]]} \N{LESS-THAN OR EQUAL TO} "
f"{result_types[res_idx[2]]}",
)
if __name__ == "__main__":
main()
(Знаю-знаю, этот код не очень чистый и имеет повторы, но он предназначен для целей тестирования, и в итоге у меня получится один из алгоритмов) . Я попробовал запустить его с кэшем=True и кэш=False, чтобы проверить, помогает ли это в чем-то, но результаты оказались очень запутанными. Похоже, что иногда непараллельная версия работает быстрее даже при больших значениях n, а гибридная версия на самом деле ничего не улучшает. Вот пример результатов, которые я получаю:
Эти результаты очень запутанны и непоследовательны. В другом прогоне я понял, что непараллельная версия быстрее, а в другом — что параллельная версия быстрее. Похоже, я делаю что-то не так, но я не могу понять, что происходит. Почему я не вижу последовательного улучшения производительности в параллельной версии, особенно для больших значений n, и почему мой гибридный подход в большинстве случаев не улучшает производительность? Будем признательны за любое понимание того, что здесь происходит.
Изменить:
Следуя ответу @Jerome Richard, я изменил код, чтобы предварительно выделить буферы и повторно использовать их для всех моих тестов. Результаты по-прежнему кажутся мне странными: параллельная версия большую часть времени работает хуже всего, даже для больших n. Я даже включил n = 500 000 000, чтобы еще больше расширить границы (очевидно, мой компьютер не может обработать 1 000 000 000, поэтому пришлось сократить его вдвое), но параллельная версия по-прежнему работает хуже. Почему я не вижу каких-либо существенных улучшений в параллельной или гибридной версии алгоритма?
Измененный код:
from datetime import timedelta
from time import perf_counter
import numba as nb
import numpy as np
import numpy.typing as npt
Это дополнительный вопрос к вопросу, который я задавал ранее, но я думаю, что мне следует начать все сначала. Я пытаюсь реализовать симуляцию числа Пи Монте-Карло и использую numba для повышения производительности. Поскольку каждая итерация цикла независима от других, я подумал, что смогу повысить производительность с помощью Parallel=True и numba.prange. Я попробовал и понял, что для небольших значений n распараллеливание того не стоит. Я попробовал улучшенную версию, в которой я использую распараллеливание после пересечения определенного порога для n, но обнаружил, что большую часть времени она работает хуже, чем мои предыдущие попытки. Теперь у меня есть сжатие трех версий алгоритма: обычная без распараллеливания, параллельная версия с использованием numba.prange и «улучшенная» гибридная версия, использующая распараллеливание после того, как указанный порог для n равен перечеркнуто: from datetime import timedelta from time import perf_counter
import numba as nb import numpy as np import numpy.typing as npt
if jit_opts.get("cache", False): print("Using cached JIT compilation") else: print("Using JIT compilation without caching") print()
print("Using parallel count_points_in_circle") for i, n in enumerate(n_values): start = perf_counter() points, in_circle, pi_approx = monte_carlo_pi_parallel(n) end = perf_counter() duration = end - start time_results[i, 0] = duration delta = timedelta(seconds=duration) elapsed_msg = ( f"[{delta} (Raw time: {duration} s)]" if delta else f"[Raw time: {duration} s]" ) print( f"n = {n:,}:".ljust(20), f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20), elapsed_msg, )
print() print("Using non-parallel count_points_in_circle") for i, n in enumerate(n_values): start = perf_counter() points, in_circle, pi_approx = monte_carlo_pi(n) end = perf_counter() duration = end - start delta = timedelta(seconds=duration) time_results[i, 1] = duration elapsed_msg = ( f"[{delta} (Raw time: {duration} s)]" if delta else f"[Raw time: {duration} s]" ) print( f"n = {n:,}:".ljust(20), f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20), elapsed_msg, )
print() print("Improved version:") for i, n in enumerate(n_values): start = perf_counter() points, in_circle, pi_approx = monte_carlo_pi_improved(n) end = perf_counter() duration = end - start delta = timedelta(seconds=duration) time_results[i, 2] = duration elapsed_msg = ( f"[{delta} (Raw time: {duration} s)]" if delta else f"[Raw time: {duration} s]" ) print( f"n = {n:,}:".ljust(20), f"\N{GREEK SMALL LETTER PI} \N{ALMOST EQUAL TO} {pi_approx}".ljust(20), elapsed_msg, )
print() print("Comparison:") result_types = ("parallel", "non-parallel", "improved") for n, res in zip(n_values, time_results): res_idx = np.argsort(res) print( f"n = {n:,}:".ljust(20), f"{result_types[res_idx[0]]} \N{LESS-THAN OR EQUAL TO} " f"{result_types[res_idx[1]]} \N{LESS-THAN OR EQUAL TO} " f"{result_types[res_idx[2]]}", )
if __name__ == "__main__": main()
(Знаю-знаю, этот код не очень чистый и имеет повторы, но он предназначен для целей тестирования, и в итоге у меня получится один из алгоритмов) . Я попробовал запустить его с кэшем=True и кэш=False, чтобы проверить, помогает ли это в чем-то, но результаты оказались очень запутанными. Похоже, что иногда непараллельная версия работает быстрее даже при больших значениях n, а гибридная версия на самом деле ничего не улучшает. Вот пример результатов, которые я получаю: [img]https://i.sstatic.net/FZHDZ.png[/img] Эти результаты очень запутанны и непоследовательны. В другом прогоне я понял, что непараллельная версия быстрее, а в другом — что параллельная версия быстрее. Похоже, я делаю что-то не так, но я не могу понять, что происходит. Почему я не вижу последовательного улучшения производительности в параллельной версии, особенно для больших значений n, и почему мой гибридный подход в большинстве случаев не улучшает производительность? Будем признательны за любое понимание того, что здесь происходит. Изменить: Следуя ответу @Jerome Richard, я изменил код, чтобы предварительно выделить буферы и повторно использовать их для всех моих тестов. Результаты по-прежнему кажутся мне странными: параллельная версия большую часть времени работает хуже всего, даже для больших n. Я даже включил n = 500 000 000, чтобы еще больше расширить границы (очевидно, мой компьютер не может обработать 1 000 000 000, поэтому пришлось сократить его вдвое), но параллельная версия по-прежнему работает хуже. Почему я не вижу каких-либо существенных улучшений в параллельной или гибридной версии алгоритма? Измененный код: from datetime import timedelta from time import perf_counter
import numba as nb import numpy as np import numpy.typing as npt
Я изучаю OpenMP, и мне нужно внедрить его в симуляцию Монте-Карло. Однако после того, как я это реализовал, затраченное время все равно не сократилось так сильно, как ожидалось, как показано на рисунке. Мой код OpenMP используется неправильно,...
Я изучаю OpenMP, и мне нужно внедрить его в симуляцию Монте-Карло для оценки вероятности появления последовательных королей в перетасованной колоде. Однако после того, как я это реализовал, затраченное время все равно не сократилось так сильно, как...
Я работаю над движком «Крестики-нолики», используя алгоритм поиска по дереву Монте-Карло (MCTS). Однако я столкнулся с ошибкой, из-за которой ИИ иногда не может заблокировать выигрышные ходы противника, что приводит к проигрышам. Кроме того,...
Я работаю над движком «Крестики-нолики», используя алгоритм поиска по дереву Монте-Карло (MCTS). Однако я столкнулся с ошибкой, из-за которой ИИ иногда не может заблокировать выигрышные ходы противника, что приводит к проигрышам. Кроме того,...