Notifications
Clear all

[Closed] MaxScript methods performance testing

Hi guys, I’d like to run some tests on common Editable Poly functions for the sake of speed and memory optimization. I wish to know if the test structure I coded seems up to the task for fair results, and more extensive tests. Any suggestion is very welcome. Thanks.

struct PerformanceTester
(
    iStartMem = 0,
    iStartTime = 0,
    iStopMem = 0,
    iStopTime = 0,
    
    iCycle = 0,
    
    iResTime = 0,
    iResMem = 0,
    
    function start =
    (
        gc()
        iCycle += 1
        iStartMem = (heapSize - heapFree)
        iStartTime = timeStamp()    
    ),
    
    function stop =
    (
        iStopTime = timeStamp()
        iStopMem = (heapSize - heapFree)

        iResTime += ((iStopTime - iStartTime) / 1000.0)
        iResMem += ((iStopMem - iStartMem) / 1024.0)
    ),
    
    function report =
    (
        format "Average processing on % iteration(s) took:
" iCycle
        format "% seconds
" (iResTime / iCycle)
        format "% Kbytes

" (iResMem / iCycle)

        iCycle = 0
    
        iResTime = 0
        iResMem = 0
    )
)

Follows a test on one of most used polyOp methods: getVert()

-- polyOp.getVert TEST

(
    local theWatch = PerformanceTester()
    local gv = polyOp.getVert

    local theSphere = convertToPoly(Sphere segments:200)
    local iNumVerts = polyOp.getNumVerts theSphere


    for i = 1 to 100 do
    (
        theWatch.start()
        for j = 1 to iNumVerts do
        (
            polyOp.getVert theSphere j
        )
        theWatch.stop()
    )
    theWatch.report()

    -- Average processing on 100 iteration(s) took:
    -- 0.03749 seconds
    -- 1272.57 Kbytes <-- memory leak?

    
    for i = 1 to 100 do
    (
        theWatch.start()
        for j = 1 to iNumVerts do
        (
            gv theSphere j
        )
        theWatch.stop()
    )
    theWatch.report()

    -- Average processing on 100 iteration(s) took:
    -- 0.02917 seconds <-- greater speed
    -- 0.226563 Kbytes <-- lower memory consumption


    delete theSphere
)
  • Enrico
2 Replies

looks good to me man…only thing I would add maybe is a gc call in the stop method too – grab the memory pre-garbage collection & post-garbage collection to make sure the leak is collectable at least or if it causes errors.

Another thing I’ve noticed is leaks often cause an Unknown System Exception when garbage collecting, so this is a good way to make sure whatever code you’re running (not just poly ops) are not causing errors.

I’m going to use this to test the python system…thanks!

Hi Eric, thank you for the quick reply. I made some adjustments as you suggested, hoping it’s exactly what you meant. It seems polyOp.getVert() doesn’t lead to a memory leak, or at least it is collectible and doesn’t clutter the heap. I’m really glad this little structure could help you testing Python performances within 3ds Max. Thank you for the hard work!

struct PerformanceTester
(
    iStartTime = 0,
    iStartMem = 0,
    
    iStopTime = 0,
    iStopMem = 0,
    iStopMemDelta = 0,

    iCycle = 0,
    
    iResTime = 0,
    iResMem = 0,
    iResMemDelta = 0,
        
    function start =
    (
        gc()
        iCycle += 1
        iStartTime = timeStamp()    
        iStartMem = heapSize - heapFree
    ),
    
    function stop =
    (
        iStopTime = timeStamp()
        iResTime += (iStopTime - iStartTime) / 1000.0

        iStopMem = heapSize - heapFree
        iResMem += (iStopMem - iStartMem) / 1024.0

        gc()
        iStopMemDelta = heapSize - heapFree
        iResMemDelta += (iStopMemDelta - iStartMem) / 1024.0
    ),
    
    function report =
    (
        format "Average processing on % iteration(s) took:
" iCycle
        format "% seconds
" (iResTime / iCycle)
        format "% Kbytes used
" (iResMem / iCycle)
        format "% Kbytes delta

" (iResMemDelta / iCycle)

        iCycle = 0
        iResTime = 0
        iResMem = 0
        iResMemClean = 0   
    )
)
-- polyOp.getVert TEST

(
    theWatch = PerformanceTester()
    local gv = polyOp.getVert

    local theSphere = convertToPoly(Sphere segments:200)
    local iNumVerts = polyOp.getNumVerts theSphere


    for i = 1 to 100 do
    (
        theWatch.start()
        for j = 1 to iNumVerts do
        (
            polyOp.getVert theSphere j
        )
        theWatch.stop()
    )
    theWatch.report()

    -- Average processing on 100 iteration(s) took:
    -- 0.04045 seconds
    -- 1248.16 Kbytes used
    -- -0.0508594 Kbytes delta

    
    for i = 1 to 100 do
    (
        theWatch.start()
        for j = 1 to iNumVerts do
        (
            gv theSphere j
        )
        theWatch.stop()
    )
    theWatch.report()

    -- Average processing on 100 iteration(s) took:
    -- 0.02873 seconds
    -- 0.226563 Kbytes used
    -- -0.103906 Kbytes delta


    delete theSphere
)
  • Enrico