psychopy.org | Reference | Downloads | Github

Speeding up with Threading or Multiprocessing?

Hey there,

I am working on a Psychopy programm on a windows machine which presents similar stimuli in a row. So, the programm reads in a text file with some parameters about the shape of the stimulus (e.g. rectangle-shaped stripe with 10 degrees width at 90 degrees orientation) or the stimulus color and timing. I use the ShapeStim class for the stripes. The approx.10-20 different stripes should then be presented subsequently.

All of this works, but I have some timing issues. When finishing a presentation of a stripe and a new stripe is generated, the parameters are modified and then presented in the opened window. This takes about 30 ms of delay and this should not happen in a usual experimental flow.
I read the advices for better timing, so then I did not create a new ShapeStim for every stripe, instead I reused the old one and just modified the parameters. But this still caused the 3-5 ms delay.
So I tried doing Threading, instead of reusing the ShapeStim object. In a thread I prepare the next stripe while the current stripe is being presented (this usually takes 2-4 seconds, so there is plenty of time)

This is within my main() function:

def main():
  # do a lot of initializing stuff, getting data aquisition started ....
...

  if FIRST:
    stripe = CylinderStripe(window,stripe_attributes,stripe_number)
    First = False
  else:
    stripe = next_stripe.get()
                    
  # Threading, set the next stripes parameter within __init__ of class 'CylinderStripe'
  pool = ThreadPool(processes = 1)
                
  next_number = (stripe_number+1) % stripes_no
  next_stripe = pool.apply_async(CylinderStripe, (window,stripe_attributes,next_number))

  # Draw the current stripe             
  output_data = stripe.draw(global_clock)

There is a module called CylinderStripe which sets the stripe attributes in it’s init function:

import helper

class CylinderStripe(object):

   def __init__(self,window, stripe_attributes, stripe_number):
        # The same for all classes
        self.win = window
        self.number = stripe_number 
        self.attributes = stripe_attributes
        self.win.color = helper.set_color(self.attributes.COLOR)
        
        self.rect = visual.ShapeStim(self.win, units='',fillColorSpace='rgb', lineWidth=0) 

        # stripe attributes
        self.rect.vertices = self.attributes.vertices
        self.rect.pos = (self.attributes.xpos, self.attributes.ypos)
        self.rect.fillColor = helper.set_fgcol(self.attributes.FGCOLOR)
        self.rect.ori = self.attributes.orientation
        
        # set timing
        self.min_duration = self.attributes.min_duration 
        self.duration = self.attributes.duration

   def draw(self,global_clock):

        # As long as duration, draw the stimulus
        for frameN in range(int(self.duration*FRAMERATE)):

            # Move Stimulus for a certain time
            if self.attributes.xvelocity != 0 and global_clock.getTime() <= self.min_duration:
                    self.rect.pos += (self.attributes.xvelocity,0)
                   
            # Finally, present stimulus
            self.rect.draw() 

            # process some information for output_data

            self.win.flip()

        return output_data           

But with this technique there is about 20 ms delay. I am confused, because I thought that threading could cure my problem via parallelization. Do I do something wrong ? I havent applied threading or multiprocessing before. Or maybe there is another idea to speed up the creation of another stripe ?

Thank you in advance!

Hello, it doesn’t sound like what you ware wanting to do (draw 10-20 rectangles) should cause any timing problems, and multithreading shouldn’t be necessary at all. But it is a bit hard to decode your drawing loop, as you seem to have the win.flip()call embedded within each stimulus’ .draw()method.

I’m struggling to see how you actually draw multiple stimuli simultaneously, if each stimulus contains its own duration code and hogs the win.flip()to itself?

Threading is not the answer here - drawing cannot be performed on multiple threads due to OpenGL.
The main problem (Mike points to additional things that may also be issues) is that you’re trying to change and draw() your stimulus in a loop but that’s a relatively slow process in interpreted languages like Python and Matlab. The answer is that you need to use arrays instead of loops (in this case you need ElementArrayStim, as shown in the provided demos). The idea is to create an array of stimuli that can be manipulated as a set rather than iteratively in a loop.

Hope that helps. I’ll be talking about these sort of efficiency improvements at the intermediates programming workshop in the summer (dates still TBC).

Hey Michael and Jon,

sorry for the confusing code!

I actually call within a loop (which is is my main(), not visible for you) the subsequent stimuli one after another has ended. Each call goes to an Object of CylinderStripe which will be drawn by calling it’s draw method. It seemed more elegant to me to put the detailed stimulus coding in another module/ a class, as I will add some other classes like Circles, so the main() function won’t be crowded with several different stimuli. I made some tests and I think that this ‘outsourcing’ is not the reason for the delay. Those stimuli are not drawn simultaneously, I just wanted to prepare the attributes of the following stimulus to save some time until it’s actually being presented.
I also read about that threading has some problems with OpenGL but I thought this could be possible as long as I don’t draw or flip anything in that thread.

As you both suggested, threading is probably not the best option to take. I also read about ElementArrayStim, I guess I’ll try that out, thank you!