在本文中,我们将了解如何通过使用 Joblib 模块在 Python 中并行执行代码来大幅减少大型代码的执行时间。
Joblib模块简介
Joblib是一个用于Python的开源库,它提供了一些用于并行计算和内存映射的工具,旨在提高科学计算和数据分析的效率。
Python 中的 Joblib 模块特别用于使用 Pipelines 并行执行任务,而不是一个接一个地顺序执行任务。Joblib 模块允许用户通过利用设备中存在的所有内核来充分发挥设备的潜力,使过程尽可能快。Joblib还允许用户通过将结果存储在缓存中来使用上次缓存的结果,这样任何进程的执行速度都可以大大降低。然后我们还可以同时并行运行多个作业,尽管可以并行运行的作业数量受到当时 CPU 中可用的空闲核心数量的限制。
Joblib 模块不仅可用于从设备上的任何位置转储和加载不同的结果、数据集、模型等,例如 Pickle 模块,而且我们还可以简单地传递路径和文件名来加载或转储它。Joblib 还提供了一种压缩大型数据集的方法,以便于加载和操作。Joblib 模块中可用的不同压缩方法有 Zlib 和 LZ4,在转储数据集时,我们需要将压缩类型作为 Joblib 模块转储方法的参数。文件将以我们使用的压缩扩展名存储,Zlib 压缩为 .zlib,Lz4 压缩为 .zl4。
主要特点:
- 并行计算:Joblib提供了一个高级的并行化工具,使用户能够轻松地将计算任务分配到多个CPU核心上执行。这可以显著加速处理大规模数据集和复杂计算任务的速度。同时,Joblib还提供了一些用于并行化内循环的工具,以提高数组操作的效率。
- 内存映射:Joblib提供了一些内存映射工具,使用户能够将大型数组或数据集存储在磁盘上,并在需要时按需加载到内存中。这有助于克服内存限制问题,并允许用户处理比可用内存更大的数据集。通过内存映射,用户可以有效地利用磁盘空间来扩展其计算能力。
- 函数的透明磁盘缓存:Joblib还提供了函数的透明磁盘缓存功能,类似于memoize或make的功能。这意味着当使用相同的输入参数多次调用函数时,Joblib可以自动将函数的输出结果缓存到磁盘上,并在后续调用中直接加载缓存结果,从而避免重复计算。这可以大大提高计算效率,特别是在需要反复执行相同计算任务的情况下。
- 简单易用:Joblib库的设计目标是简单易用,使用户能够轻松地将其Python代码转换为并行计算模式,而无需深入了解底层并行化细节。这使得即使对于不熟悉并行计算的用户来说,也能轻松地利用多核处理器和分布式计算资源来提高计算速度。
- 对NumPy数组的优化:Joblib库对NumPy数组进行了特定的优化,使其在处理大规模NumPy数组时具有更高的性能和效率。这使得Joblib成为科学计算和数据分析领域中的一个强大工具。
总的来说,Joblib是一个功能强大的Python库,它提供了一系列工具和技术,使用户能够更高效地处理大规模数据集和复杂计算任务。通过并行计算、内存映射和函数缓存等功能,Joblib帮助用户克服了内存限制问题,提高了计算速度和数据访问速度,从而加速了科学计算和数据分析的过程。
安装
pip install joblib
实践步骤
首先,我们将从 joblib 模块和 time 模块导入所需的类。
import time
from joblib import Parallel,delayed
import math
t1 = time.time()
# Normal
r = [math.factorial(int(math.sqrt(i**3))) for i in range(100,1000)]
t2 = time.time()
print(t2-t1)
输出
5.053828239440918
这里我们导入 joblib 模块的并行类和延迟类,然后首先我们将检查该操作通常需要多少时间来执行。在这里,我尝试首先找出 100 到 999 之间每个数字的立方的平方根,然后找到它们的阶乘,用户可以尝试任何其他操作,但使其尽可能复杂以获得更好的结果。
现在我们将使用 difflib 模块的并行和延迟函数来尽可能减少这个时间。
两核心
使用并行功能,我们将使用 2 个核心来执行此代码并延迟阶乘函数。
import time
from joblib import Parallel,delayed
import math
t1 = time.time()
# 2 Core
r1 = Parallel(n_jobs=2)(delayed(math.factorial) (int(math.sqrt(i**3))) for i in range(100,1000))
t2 = time.time()
print(t2-t1)
输出
3.4186928272247314
这里,Parallel 函数接受一个参数 n_jobs,我们必须在其中传递我们想要使用的核心数量,或者我们将使用多少个管道来并行执行代码。之后,我们延迟 math.factorial 函数,以便它与每个管道并行工作,然后我们传递主操作。请记住始终在延迟内使用最外部的函数,对于本示例,如果我们在延迟内使用 math.sqrt 那么这不会给出更好的结果,因为我们正在使用 sqrt 的结果来做其他事情。
三核心
import time
from joblib import Parallel,delayed
import math
t1 = time.time()
# 3 Core
r1 = Parallel(n_jobs=3)(delayed(math.factorial) (int(math.sqrt(i**3))) for i in range(100,1000))
t2 = time.time()
print(t2-t1)
输出
3.0704777240753174
四核心
import time
from joblib import Parallel,delayed
import math
t1 = time.time()
# 4 Core
r1 = Parallel(n_jobs=4)(delayed(math.factorial) (int(math.sqrt(i**3))) for i in range(100,1000))
t2 = time.time()
print(t2-t1)
输出
2.864224910736084
使用所有核心
现在,如果有人不知道他们的设备有多少个核心,但想使用尽可能多的核心,那么他们将提供 -1 作为 n_jobs 参数的值。
import time
from joblib import Parallel,delayed
import math
t1 = time.time()
# Max Core
r1 = Parallel(n_jobs=-1)(delayed(math.factorial) (int(math.sqrt(i**3))) for i in range(100,1000))
t2 = time.time()
print(t2-t1)
输出
2.7631096839904785