Inconsistent duration of draw() process

Hi all,

I work on a project where we aim at drawing shape based on csv reading. Code is working (basically, it opens csvfile then read data line by line and update size and color of an ellipse for each line). However, when I check for duration consistancy of my code, I can see that :

  1. duration is not accurate : I’m reading a 1000 lines csv sheet, with frameRate set at 100, so my code in draw() loop is supposed to last for 10 sec --> when I test with time.time() function, I can see my draw() loop is around 11.4 sec
  2. duration is not consistant : sometimes, my draw() loop is around 11.4 sec, sometimes 11.7 sec, and so on…

Can someone tell me how to optimize my code to be accurate on reading and update, and consistant please ?

Here is my code :

import csv
import time

global reader
global data
global i

i=0

# path for data to read
filename = '/Users/yannickdaviaux/Desktop/essais'

# open data file to read
with open(filename) as csvfile:
    data = csvfile.readlines()

# define vizualisation window setup
def setup():
    size(640, 360)
    noSmooth()
    frameRate(100) 
    background(0)

start_time = time.time()

# draw processing animation    
def draw():
    global i
    global data
    global c
    global d
    if i < 1000:
        c = map(float(data[i]),float(min(data)),float(max(data)),0,175)
        d = map(float(data[i]),float(min(data)),float(max(data)), 40, 300)
        fill(255,c,0)
        ellipse(width/2,height/2,d,d)
        i = i+1
        if i == 1000:
            end_time = time.time()
            elapsed = end_time - start_time
            print(elapsed)

-a- in both your posts your wording sounds like
that you read a line from file / do something with it / wait next draw loop

i hope you are aware that that is not the case,
the whole file is read to a array,
might cause a start delay, but that has not relevance for the execution of draw loop.


-b- so you want have 100 FPS ( sorry no idea how python works ) but lets assume

size(640, 360)

in JAVA2D default mode
and you do some math and some drawing in each loop.
( you try FX2D? )

and now you see that the real FPS is lower ( what is normal and depends on so many factors )


i would recommend that you start with
experimenting
set framerate 60 ( default ) read your resulting timing

and go up until you see that framerate real can not reach your setpoint


-c- your program flow / used math
as far i see you calculate the min ( of 1000 values ) and max ( of 1000 values )
2000 times ( about 4 000 000 operations ( in that 10 sec ) )

while getting min and max to a variable prior to the draw loop ( like in end of setup to a global )
would avoid that completely!

2 Likes

Hi kll,

thanks for your insight ! So :

-a- that’s right, thank to notice. However, as you said, it is not relevant with the duration evaluation of the draw loop.

-b- ok I made a test and it seems it lowers my “lag” issue, but there is still a 1 to 3-sec lag out of a 10 min data signal…

-c- well you’re right, that was not very well optimized ! it’s now updated :wink: thank you

So there is still lag issue, any other ideas ?

i play here

global start_t

def setup():
    global i
    global start_t
    size(640, 360,FX2D)
    noSmooth()
    frameRate(100) 
    background(0)
    i=0
    start_t = millis()

def draw():
    global i
    global start_t
    if i < 1000:
        c = random(0,175)
        d = random(40, 300)
        fill(255,c,0)
        ellipse(width/2,height/2,d,d)
        i += 1
        if i == 1000:
            end_t = millis()
            elapsed = end_t - start_t
            print(start_t,end_t,elapsed," msec, FPS: ",(1000000.0/elapsed))

and would not complain,
but not see what would happen in 10 min

anyhow i think it is also a concept question
connecting the FPS with a loopcounter could add up errors,
also note that any hicks of your computer in that 10 min ( start other program / browser… )
would add up,
while the show you are doing / a 100 frame/sec flickering / is nothing we can SEE
so can not be relevant

so if the absolute time is your target you must run it as a timer program
but the counter way of visualization you can not do.


or just to show what i am talking about ( un-stressing the loop )
why not run at 50 FPS and show 2 rows per loop …( or every second only)


2 Likes

As @kll has noted, you cannot guarantee a consistent frame rate. Games use techniques, such as delta timing, to accommodate fluctuating frame rates. You’ve probably noticed how a video game will drop frames to keep up with play?

I’m not sure what you intend to do with this code, or why you require such accuracy. I’ve provided answers for two scenarios.

Pre-rendered
Does the sketch have to render in real-time? In other words, could you instead run your sketch and save every frame using the saveFrame() function, then combine the frames into a video format (using something like FFmpeg). A video should run at the same frame rate every time you play it.

Real-time
For real-time rendering – i.e. running the animation in Processing – you could devise a solution for dropping frames; maybe retrieving CSV entries based on how many milliseconds have elapsed:

# generate a list of numbers to represent some csv data
csv_data = range(10000)

def draw():
    background(0)
    # an arbitrary calculation to cause an erratic framerate
    for i in range(ceil(random( 2000 ))):
        for j in range(i): atan(12345*i) * tan(67890*i)
    # retrieve csv data based on milliseconds
    if millis() <= len(csv_data):
        m = millis()
        circle(width/2, height/2, csv_data[m]/10)
        print( 'ms count: {}'.format(m) )
        print( 'csv data: {}\n'.format(csv_data[m]) )
    # stop drawing after ten seconds
    else:
        noLoop()
        text('end', width/2, height/2)

The print lines confirm that the milliseconds correspond to the relevant CSV data. You can mess around with some math to get the millisecond-to-row ratio correct.

3 Likes