<p>我怀疑即使是你最棒的列表也足够大,以至于多处理可以改善计时。使用numpy和<em>多线程</em>可能是最好的选择</p>
<p>多处理引入了相当多的开销并增加了内存消耗,就像前面正确提到的@Frank Merrow一样。
不过,对于多线程来说,情况并非如此。重要的是不要混淆这些术语,因为进程和线程是不同的。
同一进程内的线程共享内存,不同进程则不共享</p>
<p>在Python中使用多核的问题是<a href="https://wiki.python.org/moin/GlobalInterpreterLock" rel="nofollow noreferrer">GIL</a>,它不允许多个线程(在同一进程中)并行执行Python字节码。一些C扩展(如numpy)可以释放GIL,这可以通过多线程从多核并行中获益。在这里,您有机会通过使用numpy在巨大改进的基础上获得一些加速</p>
<pre><code>from multiprocessing.dummy import Pool # .dummy uses threads
import numpy as np
r = np.random.RandomState(42).randint(0, 25000000000, 100_000_000)
n_threads = 8
result = np.unique(np.concatenate(
Pool(n_threads).map(np.unique, np.array_split(r, n_threads)))
).tolist()
</code></pre>
<p>使用numpy和线程池,拆分数组,使子数组在单独的线程中唯一,然后连接子数组并使重新组合的数组再次唯一。
由于在子阵列中只能识别<em>本地</em>重复项,因此有必要最终删除重组阵列的重复项</p>
<p>对于<strong>低熵数据</strong>(许多重复项),使用<a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.unique.html" rel="nofollow noreferrer">^{<cd1>}</a>代替<code>numpy.unique</code>可以快得多。与<a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.unique.html" rel="nofollow noreferrer">^{<cd2>}</a>不同,它还保留了外观的顺序</p>
<p>请注意,只有当numpy函数通过调用低级数学库而不是多线程时,使用上述线程池才有意义。所以,总是测试它是否真的提高了性能,不要想当然</p>
<hr/>
<p>在以下范围内使用100M随机生成的整数进行测试:</p>
<ul>
<li>高熵:0-25_000_000_000(199560个副本)</li>
<li>低熵:0-1000</li>
</ul>
<h2>代码</h2>
<pre><code>import time
import timeit
from multiprocessing.dummy import Pool # .dummy uses threads
import numpy as np
import pandas as pd
def time_stmt(stmt, title=None):
t = timeit.repeat(
stmt=stmt,
timer=time.perf_counter_ns, repeat=3, number=1, globals=globals()
)
print(f"\t{title or stmt}")
print(f"\t\t{min(t) / 1e9:.2f} s")
if __name__ == '__main__':
n_threads = 8 # machine with 8 cores (4 physical cores)
stmt_np_unique_pool = \
"""
np.unique(np.concatenate(
Pool(n_threads).map(np.unique, np.array_split(r, n_threads)))
).tolist()
"""
stmt_pd_unique_pool = \
"""
pd.unique(np.concatenate(
Pool(n_threads).map(pd.unique, np.array_split(r, n_threads)))
).tolist()
"""
# -
print(f"\nhigh entropy (few duplicates) {'-' * 30}\n")
r = np.random.RandomState(42).randint(0, 25000000000, 100_000_000)
r = list(r)
time_stmt("list(set(r))")
r = np.asarray(r)
# numpy.unique
time_stmt("np.unique(r).tolist()")
# pandas.unique
time_stmt("pd.unique(r).tolist()")
# numpy.unique & Pool
time_stmt(stmt_np_unique_pool, "numpy.unique() & Pool")
# pandas.unique & Pool
time_stmt(stmt_pd_unique_pool, "pandas.unique() & Pool")
# -
print(f"\nlow entropy (many duplicates) {'-' * 30}\n")
r = np.random.RandomState(42).randint(0, 1000, 100_000_000)
r = list(r)
time_stmt("list(set(r))")
r = np.asarray(r)
# numpy.unique
time_stmt("np.unique(r).tolist()")
# pandas.unique
time_stmt("pd.unique(r).tolist()")
# numpy.unique & Pool
time_stmt(stmt_np_unique_pool, "numpy.unique() & Pool")
# pandas.unique() & Pool
time_stmt(stmt_pd_unique_pool, "pandas.unique() & Pool")
</code></pre>
<p>正如您在下面的计时中所看到的,仅使用numpy而不使用多线程已经是最大的性能改进。还请注意<code>pandas.unique()</code>对于许多重复项来说比<code>numpy.unique()</code>(仅)快</p>
<pre class="lang-none prettyprint-override"><code>high entropy (few duplicates)
list(set(r))
32.76 s
np.unique(r).tolist()
12.32 s
pd.unique(r).tolist()
23.01 s
numpy.unique() & Pool
9.75 s
pandas.unique() & Pool
28.91 s
low entropy (many duplicates)
list(set(r))
5.66 s
np.unique(r).tolist()
4.59 s
pd.unique(r).tolist()
0.75 s
numpy.unique() & Pool
1.17 s
pandas.unique() & Pool
0.19 s
</code></pre>