我已经实现了一种遗传搜索算法,并尝试将其并行化,但性能却很差(比单线程更差)。我怀疑这是由于通信开销造成的。
我在下面提供了伪代码,但实质上遗传算法会创建一个大型的“染色体”对象池,然后运行多次迭代:
评分算法(步骤1)是主要的瓶颈,因此分发处理此代码似乎很自然。
我遇到了一些我希望可以得到帮助的问题:
map()
传递给评分函数的对象相关联,即将每个持有得分的Future
链接回Chromosome
?我已经通过calculate_scores()
方法返回对象以非常笨重的方式完成了这项工作,但实际上我需要的是,如果有更好的方法来维护链接,则返回float
。 map()
需要很长时间才能遍历所有对象。但是,与单线程版本相比,后续调用draw_chromosome_from_pool()
非常的速度慢到我还没有看到它完成的程度。我不知道是什么导致了这个,因为该方法总是在单线程版本中快速完成。是否有一些IPC继续将染色体拉回到本地过程,即使在所有的期货已经完成之后?本地流程是否以某种方式优先排序?理想情况下,我认为(但需要更正)我想要的是一个分布式架构,其中每个工作人员在“永久”基础上本地保存Environment()
数据,然后分发Chromosome()
实例数据在迭代之间几乎没有重复的来回重复的染色体()数据得分。
很长的帖子,所以如果你花时间阅读这篇文章,那就谢谢你了!
class Chromosome(object): # Small size: several hundred bytes per instance
def get_score():
# Returns a float
def set_score(i):
# Stores a a float
class Environment(object): # Large size: 20-50Mb per instance, but only one instance
def calculate_scores(chromosome):
# Slow calculation using attributes from chromosome and instance data
chromosome.set_score(x)
return chromosome
class Evolver(object):
def draw_chromosome_from_pool(self, max_score):
while True:
individual = np.random.choice(self.chromosome_pool)
selection_chance = np.random.uniform()
if selection_chance < individual.get_score() / max_score:
return individual
def run_evolution()
self.dask_client = Client()
self.chromosome_pool = list()
for i in range(10000):
self.chromosome_pool.append( Chromosome() )
world_data = LoadWorldData() # Returns a pandas Dataframe
self.world = Environment(world_data)
iterations = 1000
for i in range(iterations):
futures = self.dask_client.map(self.world.calculate_scores, self.chromosome_pool)
for future in as_completed(futures):
c = future.result()
highest_score = max(highest_score, c.get_score())
new_pool = set()
while len(new_pool)<self.pool_size:
mother = self.draw_chromosome_from_pool(highest_score)
# do stuff to build a new pool
答案 0 :(得分:3)
是的,每次拨打电话
futures = self.dask_client.map(self.world.calculate_scores, self.chromosome_pool)
你正在序列化self.world
,这很大。你可以在循环之前用
future_world = client.scatter(self.world, broadcast=True)
然后在循环中
futures = self.dask_client.map(lambda ch: Environment.calculate_scores(future_world, ch), self.chromosome_pool)
将使用工作者已经存在的副本(或者使用相同的简单函数)。关键是future_world
只是指向已经分发的东西的指针,但是dask会为你处理这个问题。
关于哪个染色体是哪个问题:使用as_completed
会破坏您将其提交到map
的顺序,但这不是您的代码所必需的。您可以在完成所有工作时使用wait
进行处理,或者只是遍历future.result()
(将等待每项任务完成),然后您将保留在chromosome_pool。