Тестирование производительности: Julia, MATLAB, Python
Сравнение производительности Julia, MATLAB и Python
Введение
При выборе инструмента для инженерных расчётов важны не только удобство и количество библиотек, но и скорость выполнения вычислений.
MATLAB считается стандартом для научных расчётов, Python получил широкое распространение благодаря экосистеме NumPy и SciPy, а Julia была создана специально для высокопроизводительных вычислений.
В данном примере мы сравним производительность Julia, MATLAB и Python на нескольких типичных инженерных задачах:
-
векторные вычисления;
-
матричные операции;
-
решение системы линейных уравнений;
-
вычисление быстрого преобразования Фурье (FFT);
-
численное интегрирование;
-
Метод Монте-Карло для нахождения числа π;
-
Решение уравнения теплопроводности.
Во всех тестах измерим время выполнения и сравним результаты.
Для объективности, измерение времени будем проводить с помощью встроенных инструментов:
@belapsedв Julia;
tic … toc в MATLAB;
time.perf_counter()в Python.
Подключение библиотек
Для проведения тестов, нам потребуются следующие библиотеки:
MATLAB— вызов MATLAB из Julia;PyCall— взаимодействие с Python;LinearAlgebra— для матричных операций;FFTW— для быстрого преобразования Фурье;BenchmarkTools— точное измерение времени.
using MATLAB, PyCall, BenchmarkTools, LinearAlgebra, FFTW
Подготовка Python-модулей
Подключим необходимые Python-библиотеки.
np = pyimport("numpy")
scipy_integrate = pyimport("scipy.integrate")
Тест 1. Векторные вычисления
Вычислим выражение
для массива из 10 миллионов элементов.
Данное выражение специально выбрано как пример интенсивных поэлементных вычислений.
N = 10^7
x = rand(N)
Julia
julia_time = @belapsed sin.($x).^2 .+ cos.($x).^2
println("Julia: ", julia_time, " секунд")
MATLAB
Передадим данные в MATLAB и измерим время выполнения
@mput x
mat"""
tic;
y = sin(x).^2 + cos(x).^2;
matlab_time = toc;
"""
@mget matlab_time
println("MATLAB: ", matlab_time, " секунд")
Python
py_x = PyObject(x)
py"""
import time
import numpy as np
start = time.perf_counter()
y = np.sin($py_x)**2 + np.cos($py_x)**2
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
Julia демонстрирует наименьшее время исполнения благодаря JIT-компиляции, генерирующей машинный код без накладных расходов на интерпретацию.
Тест 2. Произведение больших матриц
Сгенерируем две случайные матрицы размером 5000×5000 и выполним их умножение.
Это один из наиболее распространённых инженерных тестов.
A = rand(5000, 5000)
B = rand(5000, 5000)
Julia
julia_time = @belapsed $A * $B
println("Julia: ", julia_time, " секунд")
Matlab
@mput A B
mat"""
tic;
C = A * B;
matlab_time = toc;
"""
@mget matlab_time
println("Matlab: ", matlab_time, " секунд")
Python
pyA = PyObject(A)
pyB = PyObject(B)
py"""
tmp = $pyA @ $pyB
"""
py"""
import timeit
python_time = timeit.timeit( lambda: $pyA @ $pyB, number=1)
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
Результаты Julia и MATLAB близки, тогда как Python заметно уступает в скорости организации вызовов.
Тест 3. Решение системы линейных уравнений
Решим систему уравнений:
для матрицы размером 8000×8000.
A = rand(8000, 8000)
b = rand(8000)
Julia
julia_time = @belapsed A\b
println("Julia: ", julia_time, " секунд")
MATLAB
@mput A b
mat"""
tic;
x = A\\b;
matlab_time = toc;
"""
@mget matlab_time
println("Matlab: ", matlab_time, " секунд")
Python
pyA = PyObject(A)
pyb = PyObject(b)
py"""
import time
import numpy as np
start = time.perf_counter()
x = np.linalg.solve($pyA, $pyb)
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
Идентичный характер производительности Julia и MATLAB; Python немного уступает так же как и в матричном умножении.
Тест 4. Быстрое преобразование Фурье
Вычислим FFT сигнала длиной 10 миллионов отсчётов.
signal = rand(10^7)
Julia
fft(signal)
julia_time = @belapsed fft(signal)
println("Julia: ", julia_time, " секунд")
Matlab
@mput signal
mat"""
tic;
Y = fft(signal);
matlab_time = toc;
"""
@mget matlab_time
println("Matlab: ", matlab_time, " секунд")
Python
py_signal = PyObject(signal)
py"""
import time
import numpy as np
start = time.perf_counter()
Y = np.fft.fft($py_signal)
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
В данном тесте MATLAB показывает наилучший результат; Julia немного уступает, Python демонстрирует приемлемую, но значительно меньшую скорость.
Тест 5. Численное интегрирование
Постановка задачи
Вычислим интеграл
методом трапеций на сетке из 100 миллионов точек.
N = 10^8
Julia
Определим функцию интегрирования.
function integrate_julia(N)
h = 1000 / N
s = 0.0
@inbounds for i in 1:N
x = i*h
s += sin(x)*cos(x)*exp(-x/1000)
end
return s*h
end
julia_time = @belapsed integrate_julia(N)
println("Julia: ", julia_time, " секунд")
MATLAB
@mput N
mat"""
N = double(N);
tic;
h = 1000 / N;
s = 0.0;
for i = 1:N
x = i * h;
s = s + sin(x) * cos(x) * exp(-x / 1000);
end
result = s * h;
matlab_time = toc;
"""
@mget matlab_time
println("MATLAB: ", matlab_time, " секунд")
Python
py"""
import math
import time
def integrate_python(N):
h = 1000/N
s = 0.0
for i in range(N):
x = i*h
s += math.sin(x)*math.cos(x)*math.exp(-x/1000)
return s*h
start = time.perf_counter()
integrate_python(100000000)
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
Julia почти вдвое превосходит MATLAB и на порядок — Python, что наглядно показывает эффективность компиляции последовательных циклов.
Тест 6. Метод Монте-Карло для вычисления числа π
Сгенерируем 100 миллионов случайных точек внутри квадрата и определим, сколько из них попадает в единичную окружность.
Это классический пример, где требуется огромное количество простых вычислений в цикле.
Julia
function montecarlo_pi(N)
inside = 0
@inbounds for i in 1:N
x = rand()
y = rand()
if x*x + y*y <= 1.0
inside += 1
end
end
return 4 * inside / N
end
julia_time = @belapsed montecarlo_pi(100_000_000)
println("Julia: ", julia_time, " секунд")
MATLAB
mat"""
tic
inside = 0;
for i = 1:100000000
x = rand();
y = rand();
if x*x + y*y <= 1
inside = inside + 1;
end
end
pi_est = 4 * inside / 100000000;
matlab_time = toc;
"""
@mget matlab_time
println("MATLAB: ", matlab_time, " секунд")
Python
py"""
import random
import time
def montecarlo_pi(N):
inside = 0
for _ in range(N):
x = random.random()
y = random.random()
if x*x + y*y <= 1.0:
inside += 1
return 4.0 * inside / N
start = time.perf_counter()
montecarlo_pi(100_000_000)
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
На цикле с интенсивными вызовами генератора случайных чисел Julia работает на порядок быстрее MATLAB и Python, исключая накладные расходы на итерации.
Тест 7. Решение уравнения теплопроводности
Выполним 1000 временных шагов для уравнения теплопроводности
на сетке из 100 тысяч узлов.
Это одна из типичных инженерных задач.
u = rand(10^5)
steps = 10^3
Julia
function heat1d(u, α, steps)
tmp = similar(u)
for n in 1:steps
@inbounds for i in 2:length(u)-1
tmp[i] =
u[i] +
α*(u[i+1] - 2u[i] + u[i-1])
end
u, tmp = tmp, u
end
return u
end
julia_time = @belapsed heat1d(copy($u), 0.1, steps)
println("Julia: ", julia_time, " секунд")
Matlab
@mput u
@mput steps
mat"""
u = double(u);
tmp = zeros(size(u));
tic
for n = 1:steps
for i = 2:length(u)-1
tmp(i) = u(i) + 0.1*(u(i+1) - 2*u(i) + u(i-1));
end
t = u;
u = tmp;
tmp = t;
end
matlab_time = toc;
"""
@mget matlab_time
println("MATLAB: ", matlab_time, " секунд")
Python
pyu = PyObject(u)
psteps = PyObject(steps)
py"""
import numpy as np
import time
def heat1d(u, alpha, steps):
tmp = np.empty_like(u)
for _ in range(steps):
for i in range(1, len(u)-1):
tmp[i] = (
u[i]
+ alpha*(u[i+1] - 2*u[i] + u[i-1])
)
u, tmp = tmp, u
return u
start = time.perf_counter()
heat1d($pyu, 0.1, $psteps)
python_time = time.perf_counter() - start
"""
python_time = py"python_time"
println("Python: ", python_time, " секунд")
Julia превосходит MATLAB почти на порядок, а Python — более чем на три порядка, раскрывая фундаментальное преимущество компиляции вложенных циклов.
Итоги
Составим сравнительную таблицу с результатами измерений времени вычислений.
|
Тест |
Время Julia, c |
Время Matlab, c |
Время Python, c |
|
Векторные вычисления |
0.2139 |
0.2791 |
0.3749 |
|
Произведение больших матриц |
2.1764 |
2.3193 |
7.0526 |
|
Решение системы линейных уравнений |
5.7585 |
6.0654 |
11.1721 |
|
Быстрое преобразование Фурье |
1.0116 |
0.4867 |
2.5993 |
|
Численное интегрирование |
2.7059 |
5.5843 |
25.8805 |
|
Метод Монте-Карло для вычисления числа π |
0.3290 |
9.2279 |
15.3714 |
|
Решение уравнения теплопроводности |
0.0715 |
0.6327 |
76.2126 |
По совокупности тестов Julia демонстрирует наилучшую средневзвешенную производительность, показывая паритет с MATLAB на некоторых операциях и многократное превосходство в задачах с интенсивными циклами. Python, несмотря на высокооптимизированные библиотеки, систематически уступает там, где требуется нетривиальная логика.
Заключение
Тестирование продемонстрировало высокую эффективность Julia для инженерных вычислений. В задачах, сводящихся к вызову оптимизированных библиотек (умножение матриц, решение СЛАУ), Julia находится на уровне MATLAB, опережая Python. Ключевое преимущество раскрывается в сценариях, не сводимых к чистой векторизации, — численном интегрировании, методе Монте-Карло, эволюционных схемах, — где разрыв с MATLAB достигает порядка, а с Python — двух-трёх порядков.
С инженерной точки зрения Julia в Engee особенно привлекателен: в отличие от проприетарного MATLAB, Julia — открытая технология, что в сочетании с Engee обеспечивает воспроизводимость расчётов и гибкость масштабирования; кроме того, преодолевается фундаментальное ограничение Python — низкая скорость исполнения циклического кода, критичная для имитационного моделирования. Таким образом, для широкого спектра вычислительно-ёмких инженерных задач Julia обеспечивает оптимальный баланс производительности, экономической эффективности и научной воспроизводимости.