Mira scripts are very fast, which is especially important for working with large images and data arrays. Mira provides a vast selection of optimized functions for doing multi-dimensional array processing. These are used internally by many of the script classes, such as the CImage:Add() method. However, there are situations when repetitive processing is best performed by unrolling a loop by using the Lua syntax itself. The table below lists some benchmarks of this type, plus a few cases where Mira's internal speed is utilized by the gaussdev() and fitpoly1d() functions. The test machine uses a 2GHz Intel i9-13900 CPU and Windows 11 x64.
In the benchmarks, the terms "table" and "array" both refer to Lua's "table" construct for collections. Whereas a general table may be indexed using numbers or strings and may contain sub-tables, Mira uses the term "array" for the special case of a table indexed using integers and containing only numeric values, like a[1]=55.6, a[2]=-105, etc. Lua creates an empty table (or array) using the {} syntax as shown below. Table elements are added and referenced like t[3], a[k], or cat["tom"].
For clarity, some of the benchmark scripts are not written in the most compact form. For example, adding (x,y) pairs to a least squares fit is written as
for i=1,n do
L:AddPt(i,y[i])
end
This can also be written compactly on one line, like
for i=1,n do L:AddPt(i,y[i]) end
Benchmark | Speed | ||
1 |
Loop overhead (nothing between "for" and "end") over 10
million loops: for k=1,10000000 do end |
500 million loops / sec | |
2 |
Loop overhead (nothing between "while" and "end") over 10
million loops: i = 10000000 while i > 0 do i = i-1 end |
80 million loops / sec | |
3 |
Loop overhead (nothing between "repeat" and "until") over 10
million loops: k = 0 repeat k=k+1 until k=10000000 end |
60 million loops / sec | |
4 | Create an
array of 10 million sequential numbers: t = series(10000000) | 50 million numbers / sec | |
5 |
Create an array of 10 million uniformly distributed random numbers: t = random(10000000) | 50 million numbers / sec | |
6 |
Create an array of 10 million Gaussian random numbers: t = gaussdev(10000000) | 31 million numbers / sec | |
7 |
Create a new image with 100,000,000 pixels (10,000x10,000) of 64-bit real value. Images
are held in memory (rather than being garbage collected) by saving each
image object in a table: table = {} for i = 1,10 do I = new_image() I:Create( 10000, 10000, "double") table[i] = I end | 5.9 images /sec | |
8 | Perform 100 million multiplications: n=1 m=2 for i=1,100000000 do k=n*m end | 134 million multiplications / sec | |
9 |
Perform 100 million divisions: n=1 m=2 for i=1,100000000 do k=n/m end | 125 million divisions / sec | |
10 | Perform 100 million additions: n=1 m=2 for i=1,100000000 do k=n+m end | 127 million adds / sec | |
11 | Least squares solution using a
4 term 1-dimensional polynomial to fit 1000 points. The functions gaussdev()
and series() return a lua table (1-d array), and fitpoly1d() returns a new
CLsqFit class object: L = new_lsqfit() n = 1000 y = gaussdev(n) for i=1,n do L:AddPt(i,y[i]) end L:SetBasisFunc("polynomial") nCoefs = 4 L:SetNumCoefs(nCoefs) L:Fit() or, replace all the above with: x = series(n) y = gaussdev(n) L = fitpoly1d(x,y,4) | 16,400 fits /sec |
Testing Procedure
To increase the significance of the benchmarks, the benchmarked procedure, including loops, was repeated in an outer loop of 10 to 10000 cycles, and the resulting time was divided accordingly.