New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Benchmarks against Symbolics.jl #1973
Comments
I also tried: $ ./expand1
Expanding: (1 + x + y + z)**100
204ms
number of terms: 176850 But had to kill the Julia version after about a minute. |
This is interesting! Are you thinking of comparing to SymEngine.jl or plain C++ symengine? Although outside of your scope here it would also be interesting to benchmark the various symengine wrappers to see how much overhead the various language wrappers add. |
Good idea, I was comparing against C++ because I know how to compile it correctly. I am using gmp, but I think Flint would be even faster. I just used the SymEngine.jl and got: julia> using BenchmarkTools, SymEngine
julia> vars = @vars a,b,c,d,e,f,g,h,i
(a, b, c, d, e, f, g, h, i)
julia> x = ((a+b+c+1)^20)
(1 + a + b + c)^20
julia> @benchmark y = expand(x)
BenchmarkTools.Trial: 2595 samples with 1 evaluation.
Range (min … max): 1.477 ms … 97.622 ms ┊ GC (min … max): 0.00% … 0.11%
Time (median): 1.561 ms ┊ GC (median): 0.00%
Time (mean ± σ): 1.926 ms ± 5.876 ms ┊ GC (mean ± σ): 0.02% ± 0.01%
▂ ▂▃▂▄▆▃▅▄▃▇▅▄▄▆▅▅▆▇▄▃▅▆▄▇█▅▅▄▅▃▇▃▅▄▁▁
▁▁▃▃▅▆▄▆▅▆▇██████████████████████████████████████▇█▆▅▅▄▃▂▂ ▆
1.48 ms Histogram: frequency by time 1.64 ms <
Memory estimate: 186.95 KiB, allocs estimate: 17407. So I am getting 1.5ms compared to C++'s 3ms, but it's the same order of magnitude, so probably it's correct. I then tried: julia> x = ((a+b+c+1)^100)
(1 + a + b + c)^100
julia> @benchmark y = expand(x)
BenchmarkTools.Trial: 20 samples with 1 evaluation.
Range (min … max): 208.862 ms … 347.852 ms ┊ GC (min … max): 0.00% … 9.87%
Time (median): 262.638 ms ┊ GC (median): 0.05%
Time (mean ± σ): 257.937 ms ± 39.495 ms ┊ GC (mean ± σ): 1.37% ± 3.09%
█ ▁▁
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▇██▄▄▁▁▁▁▁▁▁▁▄▁▁▁▁▁▁▁▁▁▄▄▁▁▁▁▁▁▁▁▁▁▁▁▁▄ ▁
209 ms Histogram: frequency by time 348 ms <
Memory estimate: 34.97 MiB, allocs estimate: 1899009. Which is consistent with C++ as well (the fastest is 209ms, compared to 204ms in C++). So probably my numbers above are correct. |
Here is another benchmark, which just benchmarks multiplying out two large expressions. SymEngine: julia> using BenchmarkTools, SymEngine
julia> vars = @vars a,b,c,d,e,f,g,h,i
(a, b, c, d, e, f, g, h, i)
julia> x = expand(((a+b+c+1)^20));
julia> y = expand(((a+b+c+1)^15));
julia> @benchmark z = expand(x*y)
BenchmarkTools.Trial: 2 samples with 1 evaluation.
Range (min … max): 2.294 s … 5.234 s ┊ GC (min … max): 0.00% … 0.00%
Time (median): 3.764 s ┊ GC (median): 0.00%
Time (mean ± σ): 3.764 s ± 2.079 s ┊ GC (mean ± σ): 0.00% ± 0.00%
█ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
2.29 s Histogram: frequency by time 5.23 s <
Memory estimate: 91.75 MiB, allocs estimate: 6013109. vs Julia: julia> using BenchmarkTools, Symbolics
julia> vars = @variables a,b,c,d,e,f,g,h,i
9-element Vector{Num}:
a
b
c
d
e
f
g
h
i
julia> x = expand(((a+b+c+1)^20));
julia> y = expand(((a+b+c+1)^15));
julia> @benchmark z = expand(x*y)
BenchmarkTools.Trial: 1 sample with 1 evaluation.
Single result which took 7.566 s (1.70% GC) to evaluate,
with a memory estimate of 2.02 GiB, over 4750560 allocations. So Julia is now only "7.566 / 2.294 = 3.3" times slower, which is more reasonable. |
There is also an issue with running global scope things in Julia, that there might be some unnecessary boxing/unboxing. So I tried this for Julia: julia> using BenchmarkTools, Symbolics
julia> function f()
vars = @variables a,b,c,d,e,f,g,h,i
x = expand(((a+b+c+1)^20));
y = expand(((a+b+c+1)^15));
z = expand(x*y)
end
f (generic function with 1 method)
julia> @benchmark f()
BenchmarkTools.Trial: 1 sample with 1 evaluation.
Single result which took 7.945 s (1.45% GC) to evaluate,
with a memory estimate of 2.20 GiB, over 6091435 allocations. And SymEngine: julia> using BenchmarkTools, SymEngine
julia> function f()
vars = @vars a,b,c,d,e,f,g,h,i
x = expand(((a+b+c+1)^20));
y = expand(((a+b+c+1)^15));
z = expand(x*y)
end
f (generic function with 1 method)
julia> @benchmark f()
BenchmarkTools.Trial: 2 samples with 1 evaluation.
Range (min … max): 2.902 s … 3.016 s ┊ GC (min … max): 0.01% … 0.00%
Time (median): 2.959 s ┊ GC (median): 0.00%
Time (mean ± σ): 2.959 s ± 80.544 ms ┊ GC (mean ± σ): 0.00% ± 0.00%
█ █
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁
2.9 s Histogram: frequency by time 3.02 s <
Memory estimate: 92.02 MiB, allocs estimate: 6038323. |
I also tried Julia 1.9.3:
|
Does SymEngine use a polynomial data structure to expand and convert it back to an unstructured expression tree or does it exclusively work on the unstructured expression tree? |
Here is Nemo's performance: julia> using Nemo, BenchmarkTools
julia> R, (a, b, c) = PolynomialRing(ZZ, [:a, :b, :c]);
julia> p = a+b+c+1;
julia> @btime $p^20;
258.569 μs (25 allocations: 61.62 KiB) |
It looks at the expression tree and expands the power of addition expressions faster. For eg: instead of doing |
However, it doesn't do the same optimizations that Flint/Nemo does. For eg SymEngine does
and simplifies the expression to
|
Does it use the multinomial theorem to expand power of sums? |
Yes |
Ah, so that explains it. We don't have that specialization at all. For some polynomial operations, we do a round trip to multivariate polynomial representations and back. @shashi is more familiar with this process. |
SymEngine does not have that at all. |
@YingboMa I recommend to focus on this benchmark: #1973 (comment) which makes it irrelevant how the power expansion is done. It benchmarks how quickly you can expand two arbitrary, long, expressions. |
@certik, that benchmark has two expands with a power. So, not exactly comparable. |
@isuruf I don't follow. The benchmark does |
@certik, the benchmark time includes computing |
@isuruf I don't think it does. The following lines:
First expand the powers, so x and y contain expanded powers. I checked it by printing "x" and "y" and they are indeed expanded. |
Ah, you are running the benchmark like that. Thanks. |
Here is a differentiation benchmark. Symbolics.jl:
SymEngine.jl
On this particular benchmark, SymEngine seems 35x faster. I am probably doing something wrong. If anyone can figure it out, that would be great. Once we have representative benchmarks that the Julia community agrees that they are fair, we'll add it to our benchmarks. |
I want to compare the speed of SymEngine and Symbolics.jl. I don't know if I am using Symbolics.jl correctly, so I'll document what I do here. I am using Julia 1.8 on Apple M1 Max.
I then compiled SymEngine (default cmake), and applied the following diff:
And run it:
So I am getting 3ms. I don't know if I am running both benchmarks correctly. It seems SymEngine is 80x faster, which seems that I probably do something wrong.
I asked the Julia community for help here: https://discourse.julialang.org/t/benchmarking-symbolics-jl-against-symengine-jl/103744.
The text was updated successfully, but these errors were encountered: