下面的代码是否可以并行化?我看着cyton的prange,但无法弄清楚它是如何工作的。 prange是否并行化了不同内核上的内部循环?对于下面的代码,我如何并行化它?
@cython.boundscheck(False)
def gs_iterate_once(double[:,:] doc_topic,
double[:,:] topic_word,
double[:] topic_distribution,
double[:] topic_probabilities,
unsigned int[:,:] doc_word_topic,
int num_topics):
cdef unsigned int doc_id
cdef unsigned int word_id
cdef unsigned int topic_id
cdef unsigned int new_topic
for i in xrange(doc_word_topic.shape[0]):
doc_id = doc_word_topic[i, 0]
word_id = doc_word_topic[i, 1]
topic_id = doc_word_topic[i, 2]
doc_topic[doc_id, topic_id] -= 1
topic_word[topic_id, word_id] -= 1
topic_distribution[topic_id] -= 1
for j in xrange(num_topics):
topic_probabilities[j] = (doc_topic[doc_id, j] * topic_word[j, word_id]) / topic_distribution[j]
new_topic = draw_topic(np.asarray(topic_probabilities))
doc_topic[doc_id, new_topic] += 1
topic_word[new_topic, word_id] += 1
topic_distribution[new_topic] += 1
# Set the new topic
doc_word_topic[i, 2] = new_topic
答案 0 :(得分:3)
prange使用的确是OpenMP shared-memory parallelism。因此,在一台计算机上,它将创建在不同可用核心上运行的线程,并可访问相同的内存池。
对于您显示的例程,第一步是了解哪些部分可以并行化。通常情况下,使用数据作为第一个索引i
,仅对元素i
进行操作而不是i-1
或i+1
,这会使问题可并行化。这不是这种情况,因此您需要找到一种方法使计算更加独立。
实际上找到特定的并行模式超出了SO的答案,但我会提到一些提示:
with gil
块的一部分时,可以进行Python调用。答案 1 :(得分:3)
@ PierredeBuyl的回答很好地概述了prange
做什么以及如何使用它。
这是与您的代码相关的一些具体评论:
您无法并行化外循环:
doc_topic[doc_id, topic_id] -= 1
以及其他变量和+=1
的类似变量。这些修改了在所有循环之间共享的变量,并且会导致不一致的结果。
如果您要对外部循环进行并行化,则topic_probabilities[j] = ...
会出现类似的问题。
您可以轻松地对内循环for j in xrange(num_topics):
进行并行化 - 这只会修改依赖于索引的内容' j'因此,对于修改相同数据的线程没有任何问题。 (但是,每次启动多线程区域时都会产生性能成本,因此您通常会尝试并行化外部循环,以避免这种情况 - 取决于阵列的大小,您可能无法获得多少)