JanetRossini.github.io

Lua, LSL, Blender, Python in Second Life


Project maintained by JanetRossini

Timing ... and ...

Aug 17, 2025 • [linkagesluamoverstesting]


A timing test for the table-style object creation. After that, I don’t know yet.

Here’s the test I wrote to time:

  1. An empty loop;
  2. A loop calling a function with no arguments;
  3. A loop calling a function with four conventional arguments;
  4. A loop calling a function with four table arguments.
_:test("calling sequences", function()
    function f_no_args()
    end
    function f_regular(a,b,c,d)
        local a = a
        local b = b
        local c = c
        local d = d
    end
    function f_table(parms)
        local a = parms.a
        local b = parms.b
        local c = parms.c
        local d = parms.d
    end
    local n = 16_500_000
    local t0 = os.clock()
    for i = 1, n do
    end
    local t_empty = os.clock()
    for i = 1, n do
        f_no_args()
    end
    local t_no_args = os.clock()
    for i = 1, n do
        f_regular(1,2,3,4)
    end
    local t_regular = os.clock()
    for i = 1, n do
        f_table{a=1, b=2, c= 3, d=4}
    end
    local t_table = os.clock()
    local time_empty =t_empty - t0
    local time_no_args = t_no_args - t_empty
    local time_regular = t_regular - t_no_args
    local time_table = t_table - t_regular
    print("empty", time_empty)
    print("no args", time_no_args)
    print("regular", time_regular)
    print("table", time_table, '\ntab/reg', time_table/time_regular)
    print(n/time_table)
end)

As you can see, we iterate 16,500,000 times. And the results, in seconds:

Feature: timing tests
empty   0.07401849993038923
no args 0.1592389583820477
regular 0.21819420834071934
table   1.0065955416066572  
tab/reg 4.613300917844787
16391886.62972206

I fiddled the number to get the table test to run in about one second. We can then conclude that, on my M1 Macbook Air, we can call over 16 million functions using the table format every second. Of course, who knows what that will mean in Second Life.

And we also see that the regular calling sequence is about one-fifth the speed of the table. Around 3/4 of that time is taken up just calling the function.

For our purposes here, we won’t be creating many objects using the table format, so its better expressiveness is well worth it in my view. In code that has to run fast, we’ll lean away from the tables and use regular calling sequences.

Now What?

So that’s nice. What shall we do this morning?

I think we have the DriveWheel, ConnectingRod, and CouplingRod working. Maybe this morning we should draw some pictures. That will be interesting but not require too much brain power, I hope.

Let’s try to set up a test that draws, oh, four diagrams. Once that works, we should be able to enhance it as we add more objects. We’ll also have to sort out the to_svg methods for our objects: they have not been run since the changes to calling sequence, vector, and angle, so the ones that exist will be wrong and I think Connecting Rod doesn’t even have a to_svg method yet.

First the test.

_:test("draw structure", function()
    svg._instances = {}
    local steps = 4
    local step = 1
    for angle = 0,360,360/steps do
        draw_structure(step, angle)
        step += 1
    end
    print(Shape:draw())
end)

That would almost work if we had a draw_structure function. I’ll put it inside the test for now. I suspect we’ll have a real object to put it in, someday.

Um …
This is more tricky than I thought. Excuse me while I figure it out. I’ll provide a fait accompli below, with such commentary as is fit to print.

Only a bit more hassle, and I have this picture:

The code for the picture isn’t too awful, though we’ll have some things to think about:

_:test("draw structure", function()
    function draw_structure(step, angle)
        local wheel = DriveWheel{x=12, y=2}
        local dist = 2*math.pi*2*(angle/360)
        wheel:move(dist)
        wheel:to_svg()
    end
    svg._instances = {}
    local rect = svg.Rect{x=0, y=0, width=640, 
        height=480, fill=0x191919}
    local page = svg.Group({stroke_width=0.1}, function()
        local steps = 8
        local step = 1
        for angle = 0,359,360/steps do
            local g = svg.Group({}, function()
                draw_structure(step, angle)
            end)
            g:translate(0, 5.5*(step-1) + 2)
            step += 1
        end
    end)
    page:scale(10,-10)
    page:translate(0, -48)
    print(svg.Shape:draw())
end)

Nothing to see here, just a call to a function test, passing an anonymous function containing a function draw_structure, and the anonymous function calls a function Group passing another anonymous function function, and that anonymous function calls Group again, passing yet another anonymous function, which finally calls draw_structure.

Sometime, perhaps tomorrow, perhaps not, I’ll create a simpler version of this, which I’ll do by pulling out the anonymous functions, defining them first, and calling them by name. We’ll see what we think then.

Now, the truth is, I’m probably the only person who will ever do these SVG things, and the nested functions don’t trouble me.

And the picture’s not bad either, although I think I’d prefer to draw from the top down instead of bottom up. But that is just a matter of changing this translate:

g:translate(0, 5.5*(step-1) + 2)

If I were going to do a lot of these pictures, and it is possible that I will be, I’d try to package up the page layout ideas, like the translate above, separately from the diagram drawing ideas. But we don’t really want to get into page layout: it’s a jungle.

For today, I have a start at a drawing test that I can use to draw a whole linkage. I could do it right now, but I am tired and hungry.

One Last Thought
Remind me to try creating the structure just once and moving it multiple times. As done today, I’m recreating the wheel and moving it just once for each picture. the other way will be a better test, more like what will happen in actual use.

Safe paths!