Sypder 的多个控制台比多处理更快?



我正在对定义为类的交易策略进行回测。我正在尝试选择要在模型中输入的最佳参数组合,因此我在给定的时间段上运行多个回测,尝试不同的组合。这个想法是能够选择第一代种群来输入遗传算法。似乎是多处理的完美工作!

所以我尝试了很多东西,看看什么效果更快。我打开了10个Spyder控制台(是的,我试过了(,并为每个控制台运行了一个参数组合(所有控制台同时运行(。

用于每个Spyder控制台的示例代码:

class MyStrategy(day,parameters):
# my strategy that runs on a single day
backtesting=[]
for day in days:
backtesting_day=MyStrategy(day,single_parameter_combi)
backtesting.append(backtesting_day)

然后,我尝试了使用池的多处理方式。

多处理中使用的示例代码:

class MyStrategy(day,parameters):
# my strategy that runs on a single day
def single_run_backtesting(single_parameter_combi):
backtesting=[]
for day in days:
backtesting_day=MyStrategy(day,single_parameter_combi)
backtesting.append(backtesting_day)
return backtesting
def backtest_many(list_of parameter_combinations):
p=multiprocessing.pool()
result=p.map(single_run_backtesting,list_of parameter_combinations)
p.close()
p.join()
return result
if __name__ == '__main__':
parameter_combis=[...] # a list of parameter combinations, 10 different ones in this case
result = backtest_many(parameter_combis)

我还尝试了以下方法:打开 5 个 Spyder 控制台并在 for 循环中运行该类的 2 个实例,如下所示,以及一个包含 10 个类实例的 Spyder 控制台。

class MyStrategy(day,parameters):
# my strategy that runs on a single day
parameter_combis=[...] # a list of parameter combinations
backtest_dict={k: [] for k in range(len(parameter_combis)} # make a dictionary of empty lists
for day in days:
for j,single_parameter_combi in enumerate(parameter_combis):
backtesting_day=MyStrategy(day,single_parameter_combi)
backtest_dict[j].append(backtesting_day)

令我非常惊讶的是,多处理大约需要 25 分钟才能完成一天,大约在同一时间,单个 Spyder 控制台在 for 循环中有 10 个类实例,神奇的是,当我同时运行 10 个 Spyder 控制台时,它只需要 15 分钟。如何处理这些信息?这对我来说真的没有意义。我在 Windows 12 上运行一台 10-cpu 机器。

考虑到我计划使用 96 核机器在 AWS 上运行东西,在遗传算法中交叉了大约 100 个参数组合,该算法应该运行大约 20-30 代(完整的回测是 2 个工作月 = 44 天(。

我的问题是:我错过了什么???最重要的是,这只是规模的差异吗? 我知道,例如,如果您定义一个简单的平方函数并串行运行 100 次,则多处理实际上比 for 循环慢。你开始看到大约10000倍的优势,例如:https://github.com/vprusso/youtube_tutorials/blob/master/multiprocessing_and_threading/multiprocessing/multiprocessing_pool.py

当我使用多处理进行多达 100 种组合时,我会看到性能差异吗,如果是这种情况,有什么办法可以在 advnace 中知道吗?我是否正确编写了代码?其他想法?您认为如果我在"上面"一步中使用多处理,在多天内在单个参数组合中使用多处理会显着加快速度吗?

要扩展我的评论"尝试p.imap_unordered().":

p.map()可确保按参数列表中相同的顺序获取结果。为了实现这一目标,一些工人必须闲置一段时间。 对于您的用例 - 本质上是参数组合的网格搜索 - 您真的不需要将它们按相同的顺序排列,您只想最终获得最佳选择。(此外,引用文档,"它可能会导致非常长的可迭代对象使用高内存。考虑使用 imap(( 或 imap_unordered( 和显式块大小选项来提高效率。

相比之下,p.imap_unordered()并不真正关心 - 它只是将事情排队,工人在释放时处理它们。

还值得尝试使用chunksize参数 - 引用imap()文档,"对于很长的迭代对象,使用较大的块大小值可以使作业完成速度比使用默认值 1 快得多。(因为您花在排队和同步上的时间更少(。

最后,对于您的特定用例,您可能需要考虑让主进程使用生成器函数生成无限数量的参数组合,并在找到足够好的解决方案或经过足够的时间后中断循环。

下面是一个简单的函数和一个人为的问题(找到两个随机数 0..1 以最大化它们的总和(。请记住也从worker函数返回原始参数集,否则您将无法访问它!:)

import random
import multiprocessing
import time

def find_best(*, param_iterable, worker_func, metric_func, max_time, chunksize=10):
best_result = None
best_metric = None
start_time = time.time()
n_results = 0
with multiprocessing.Pool() as p:
for result in p.imap_unordered(worker_func, param_iterable, chunksize=chunksize):
n_results += 1
elapsed_time = time.time() - start_time
metric = metric_func(result)
if best_metric is None or metric > best_metric:
print(f'{elapsed_time}: Found new best solution, metric {metric}')
best_metric = metric
best_result = result
if elapsed_time >= max_time:
print(f'{elapsed_time}: Max time reached.')
break
final_time = time.time() - start_time
print(f'Searched {n_results} results in {final_time} s.')
return best_result
# ------------
def generate_parameter():
return {'a': random.random(), 'b': random.random()}

def generate_parameters():
while True:
yield generate_parameter()

def my_worker(parameters):
return {
'parameters': parameters,  # remember to return this too!
'value': parameters['a'] + parameters['b'],  # our maximizable metric
}

def my_metric(result):
return result['value']

def main():
result = find_best(
param_iterable=generate_parameters(),
worker_func=my_worker,
metric_func=my_metric,
max_time=5,
)
print(f'Best result: {result}')

if __name__ == '__main__':
main()

运行示例:

~/Desktop $ python3 so59357979.py
0.022627830505371094: Found new best solution, metric 0.5126700311039976
0.022940874099731445: Found new best solution, metric 0.9464256914062249
0.022969961166381836: Found new best solution, metric 1.2946600313637404
0.02298712730407715: Found new best solution, metric 1.6255217652861256
0.023016929626464844: Found new best solution, metric 1.7041449687571075
0.02303481101989746: Found new best solution, metric 1.8898109980050104
0.030200958251953125: Found new best solution, metric 1.9031436071918972
0.030324935913085938: Found new best solution, metric 1.9321951916206537
0.03880715370178223: Found new best solution, metric 1.9410837287942249
0.03970479965209961: Found new best solution, metric 1.9649277383314245
0.07829880714416504: Found new best solution, metric 1.9926667738329622
0.6105098724365234: Found new best solution, metric 1.997217792614364
5.000051021575928: Max time reached.
Searched 621931 results in 5.07216 s.
Best result: {'parameters': {'a': 0.997483, 'b': 0.999734}, 'value': 1.997217}

(顺便说一下,这在chunksize=1时几乎慢了 6 倍。

相关内容

  • 没有找到相关文章

最新更新