Engee 文档
Notebook

Julia和MATLAB在矩阵运算上的性能比较

我们提出了一个计算实验的结果:估计在Julia上执行矩阵运算的次数可以让您相对于MATLAB中的相同运算加快代码速度。

让我们从准备环境开始。 我们将在发布时安装可能未包含在标准Engee软件包中的库,然后连接必要的库并将图形输出核心切换到非交互模式。

In [ ]:
]add BenchmarkTools StatsPlots
In [ ]:
using BenchmarkTools, LinearAlgebra, MATLAB, StatsPlots
gr()

有关信息,让我们比较两个系统用于执行矩阵运算的库的版本。 矩阵运算有很多库实现。 作为一项规则,这些函数的集合被统称为*******(Basic Linear Algebra Subroutines,基本线性代数例程)并且使用针对某些处理器优化的一个或另一个实现可以是比较因素之一。

In [ ]:
mat"version -blas"
Out[0]:
"Intel(R) oneAPI Math Kernel Library Version 2024.1-Product Build 20240215 for Intel(R) 64 architecture applications (CNR branch auto)"
In [ ]:
mat"maxNumCompThreads()"
Out[0]:
48.0

MATLAB和Julia不仅有BLAS的不同实现,而且还有线程数。:

In [ ]:
BLAS.get_config()
Out[0]:
LinearAlgebra.BLAS.LBTConfig
Libraries: 
└ [ILP64] libopenblas64_.so
In [ ]:
BLAS.vendor()
Out[0]:
:lbt
In [ ]:
BLAS.get_num_threads()
Out[0]:
24
In [ ]:
BLAS.set_num_threads(48)
In [ ]:
BLAS.get_num_threads()
Out[0]:
48

我们看到MATLAB使用英特尔的oneAPI库,Julia使用libopenblas64开源库。 在Engee方面,两个系统都分配了相同的资源进行测试。 我们可以开始比较它们。

矩阵乘法问题

让我们通过将所有执行结果保存到一个向量中来解决Julia问题(我们将只输出中位数结果):

In [ ]:
sizes = Int.([0.5e2, 1e2, 0.5e3, 1e3, 0.5e4, 1e4])

julia_times = []
for n in sizes
    result = @benchmark rand($n,$n) * rand($n,$n);
    append!(julia_times, [result.times ./ 1e9]) # ns to sec
end

# 让我们输出每次发射的中值时间以进行验证
median.(julia_times)
Out[0]:
6-element Vector{Float64}:
  2.2984e-5
  0.000101845
  0.428853208
  0.699654599
 12.763432786
 58.412450882

我们可以通过命令节省中间时间 Float64(median(result.times) / 1e9)) 但是看到带有公差的图形会很有趣,所以我们保存了所有的结果。 现在让我们解决MATLAB内核上的问题,这是我们可以使用的。:

In [ ]:
@mput sizes                                   # 将矩阵参数传输到MATLAB

mat"""
run_times = [];
for i = 1:length(sizes)
    n = sizes(i);
    TestFunc=@()rand(n,n)*rand(n,n);%定义用于测试的函数
    t=timeit(TestFunc);%我们测量执行时间
    run_times=[run_times,t];%保存到输出向量
end
""";

matlab_time = @mget run_times                # 从MATLAB中获取结果的向量
Out[0]:
1×6 Matrix{Float64}:
 0.000382712  0.000934712  0.0156977  0.100696  5.25263  31.8614

环境如何 @benchmark 在Julia中,函数也是如此 timeit 在MATLAB中,两者都运行被转移到多次的操作。

让我们构建一个图表,显示Julia允许我们实现的加速度。:

In [ ]:
n_points = length(sizes)
julia_means = zeros(n_points)
julia_stds = zeros(n_points)
for i in 1:n_points
    julia_means[i] = mean(julia_times[i])
    julia_stds[i] = std(julia_times[i])
end
data_matrix = [julia_means matlab_time']

boxplot([matlab_time[i]./julia_times[i] for i in 1:length(sizes)], c=1, legend=false,
      xlabel="方阵的侧面(元素)", ylabel="Julia代码运行得更快了多少次(x次)",
      xticks=(1:length(sizes),sizes), lw=2, guidefont=font(8), left_margin=15Plots.mm, size=(800,450),
      title="N\nJulia侧的两个随机方阵的乘法在小于100和超过1000个元素的矩阵上略快", titlefont=font(11))
Out[0]:
No description has been provided for this image

指数化的问题

让我们在Julia和MATLAB中测量执行时间:

In [ ]:
sizes = Int.([0.5e2, 1e2, 0.5e3, 1e3, 0.5e4, 1e4])

# 朱莉娅的测量
julia_times = []
for n in sizes
    A = rand(n,n);
    result = @benchmark $A .^3;
    append!(julia_times, [result.times ./ 1e9]) # ns to sec
end

@mput sizes                         # 将矩阵参数传输到MATLAB
mat"""
run_times = [];
for i = 1:length(sizes)
    n = sizes(i);
    A=rand(n,n);%创建随机矩阵
    testFunc=@()一个。^3;%定义用于测试的函数
    t=timeit(TestFunc);%我们测量执行时间
    run_times=[run_times,t];%保存到输出向量
end
""";
matlab_time = @mget run_times;      # 从MATLAB中获取结果的向量

让我们构建一个图表,显示使用Julia可以实现多快的矩阵乘法。:

In [ ]:
boxplot([matlab_time[i]./julia_times[i] for i in 1:length(sizes)], c=2, legend=false,
      xlabel="方阵的侧面(元素)", ylabel="Julia代码运行得更快了多少次(x次)",
      xticks=(1:length(sizes),sizes), lw=2, guidefont=font(8), titlefont=font(11),
      title="Julia加速度相对于MATLAB在中值时间\N任务:将随机矩阵的元素提升为幂", left_margin=15Plots.mm, size=(800,450))
Out[0]:
No description has been provided for this image

矩阵乘法的问题

与前面的例子类似,我们执行矩阵乘法:

In [ ]:
sizes = Int.([.5e2, 1e2, .5e3, 1e4, .5e5])

# 朱莉娅的测量
julia_times = []
for n in sizes
    A = rand(n,n); B = rand(n,n);
    result = @benchmark $A * $B;
    append!(julia_times, [result.times ./ 1e9]) # ns to sec
end

@mput sizes                          # 将矩阵参数传输到MATLAB
mat"""
run_times = [];
for i = 1:length(sizes)
    n = sizes(i);
    A=rand(n,n);B=rand(n,n);%创建随机矩阵
    testFunc=@()A*B;%定义用于测试的函数
    t=timeit(TestFunc);%我们测量执行时间
    run_times=[run_times,t];%保存到输出向量
end
""";
matlab_time = @mget run_times;       # 从MATLAB中获取结果的向量

boxplot([matlab_time[i]./julia_times[i] for i in 1:length(sizes)], c=3, legend=false,
      xlabel="方阵的侧面(元素)", ylabel="Julia代码运行得更快了多少次(x次)",
      xticks=(1:length(sizes),sizes), lw=2, guidefont=font(8), titlefont=font(11),
      title="Julia加速度相对于MATLAB在中值时间\N任务:将随机矩阵的元素提升为幂", left_margin=15Plots.mm, size=(800,450))

结论

除了图表中呈现的视觉结论之外,我们还可以看到比较MATLAB和Julia中函数的执行并得出我们自己的结论是多么容易。

分享您的意见-我们是否用一些次优的方法破坏了测试结果,并建议您的功能选项!