Do things on a thread separate of draw

edited January 2018 in How To...

I have a method update(), and I want to run it separately of draw. Basically update() has to be faster than draw(), because otherwise the program is slow. How do I do that?

Answers

  • It depends on what you want to do in the update() method. What do you want to do?

  • edited January 2018

    @quark my sketch is rather big but it's on a GitHub repo: exCircuit

    My issue is that some devices in the sketch are kinda slow. Here's an example: YouTube

    The light bulb should light up instantly but it takes a bit of delay, and each buffer gate just adds onto that delay. Each one needs about 1 frame to "process".

    I thought that putting the update functions of the devices and wires would fix this issue.

  • I suspect that the problem is that you are not updating ALL the components at the same time rather you update them one per frame which would explain

    and each buffer gate just adds onto that delay. Each one needs about 1 frame to "process".

    Using separate threads for updating might be over kill.

    I suggest you might try the pre() method. This method is always executed immediately before the draw() method. Do a complete update of all your components inside this method

    BTW there is a corresponding post() method that is executed after draw(). Read this to find out more

  • @quark you can't use pre() inside a sketch, only in a library. I tried this:

    void pre() {
      println("before draw");
    }
    

    and it doesn't do anything.

  • edited January 2018

    You can't use pre() inside a sketch, only in a library.

    You need to registerMethod() it 1st: registerMethod("pre", this);. *-:)
    As I did here: https://Forum.Processing.org/two/discussion/22272/when-does-millis-start#Item_4 ;)

  • you can't use pre() inside a sketch, only in a library.

    Although the documentation for these methods is in the library section you can use them in a sketch as pointed out by GoToLoop. It is entirely permitted and works just fine, see the sketch below for a full working example.

    int n = 10000;
    
    void setup() {
      size(300, 160);
      textSize(40);
      fill(0);
      registerMethod("pre", this);
    }
    
    void pre() {
      n++;
    }
    
    void draw() {
      background(200, 255, 200);
      text(n, 20, 100);
    }
    
  • @quark using pre() didn't change anything. I tried using enhanced loops instead of backwards loops for the devices' behavior, but it didn't change anything.

  • It is not the pre() or the fact that you aren't using threads. I believe the problem is because only one component is being updated every frame that would explain why you see the signal passing from the switch to the lamp through the buffer-gates.

    Years ago I create a logic gate simulator and the update would cycle through all the components again and again until there was no change in overall state. THEN it rendered them.

  • @quark so I don't know what to do now, I have no idea how I should implement this. I've been using enhanced loops for the updates, tried using nested loops like this:

    i=0,i<10,i++ {
        Device d : devices {
            [update code]
        }
    }
    

    (decided not to include full code, it still conveys the idea) but it didn't work.

    Can you at least give me directions, or sort of the overall idea in steps how it would work? I'm out of ideas.

  • There is an awful lot of code to look at and I can't find where you are updating the gates???

  • @quark

    Device updates are in the file e_, line 496

    Wire updates are in e___wire, line 292 (if wires have to do anything with my problem)

    Keep in mind this repo hasn't been updated yet to my local "testing" version.

  • Answer ✓

    I noticed that you have animation.

    For instance if you have a system of logic gates and then change the inputs you could resolve the system to a steady state and draw that. Alternatively you could resolve the system over a period of time by tracing the changes as the signal passes from gate to gate.

    Perhaps you could describe what you are trying to show.

  • @quark I have a rough idea of what to do now, will try and post the solution.

  • @quark I have tried tracing the pins' states, and it worked... to some extent. I tried this code:

    int dupdates = 0;
    boolean updating = true;
    while (updating && dupdates < 64) {
      updating = false;
      for (ElectronicDevice d : devices) {
        update_wires_1();
        d.defaultBehavior();
        if (!d.pinsDone) updating = true;
      }
      dupdates++;
    }
    

    (check the 1.0.0 branch in the repo for recent changes)

    In the ElectronicDevice class pinsDone is set whenever there are no more signal changes. The problem is, this doesn't work as expected. What it does instead is it updates some devices, in the next frame some more devices, until it finishes. What am I doing wrong?

  • Answer ✓

    Unfortunately I don't have the time to trace through your code trying to work out
    a) the program logic / semantics
    b) what is going wrong

    One issue is that you treat wires differently from the other devices.

    If I was to design the data model I would treat a wire as an electronic device with just one input and one output then your system would be a collection of interconnected devices rather than a collection of devices connected by wire.

    You also need to be clear on the software requirements, you still haven't made it clear what the animation is.

  • edited February 2018

    @quark Gotta say, treating wires as devices with 1I/1O pins is an approach I didn't even think of when making this program. I never made a logic simulator before and figuring out how to connect wires together was a bit of a headache for me. Thanks for the great suggestion, I think it should solve all of my problems I encountered thus far. Also this is a great opportunity for me to refactor my code quite a bit.

  • edited February 2018 Answer ✓

    One of the issues I came across when developing my logic simulator was that as it was being built it was incomplete and could comprise a number sub-networks or even isolated components.

    So I had to think how do I update
    1) ALL the controls and
    2) When do I know that I have updated as much as possible (when they are not fully connected).

    Number (1) is simple because I had a list of components to iterate over, the harder part is (2)

    Assume we an electronic device with a number of inputs and a number of outputs and we call these 'pins'. NOTE: it is better to assume more there maybe more than one output e.g. a half-adder.

    Create two classes called IPin and OPin for input and output pins respectively.

    class OPin {
      float level; // in a simple logic gate this might be just 0 or 1
      boolean resolved; // if false then we don't know the current value of level yet
                        // if true then level is correct at this time
    }
    
    class IPin extends OPin {
      OPin opin; // The output pin from another electronic device
           // will be null until we have connected it to another device
    }
    

    Notice that the output pin is unaware of what it connected to because it doesn't need to, but the input pin will collect its level from a output pin.

    This is a one-way-association between classes which is easier to manage that two-way. The only difficulty is if a device is removed because we need to null the opin field in connected ipin object.

    The final part is the update algorithm. This is the basic algorithm which will update ALL devices as far as they can be.

    set all pins and devices to unresolved
    do
      set system changed to false (assume we have gone as far as we can)
      for every unresolved device
        for every input pin
          if not resolved check the connected output pin 
            if opin is resolved then
              get level and mark the input pin as resolved
              set system changed = true
        if every input pin is resolved then
          set level for all output pins
          mark the device as resolved
          set system changed = true
    while system changed is true (go back to line 2)
    

    I admit this is a rough guide to how I would design the classes and it would probably change once I considered all the system requirements

    Thinking about it I would probably have a single Pin class

    class Pin {
      ElectronicDevice owner; // The device that owns this Pin
      float level; // in a simple logic gate this might be just 0 or 1
      boolean resolved; // if false then we don't know the current value of level yet
                        // if true then level is correct at this time
      Pin connectingPin; // The pin from another electronic device
           // will be null until we have connected it to another device
    }
    

    Don't forget the design is an abstract view of a real system, there will be many possible designs depending on the system requirements

    Good luck!

  • edited March 2018

    Yay, I fixed the issue! I have ended up with this algorithm at the end:

    int dupdates = 0;
    boolean changed;
    for (ExDevice d : devices) {
      d.resolved = false;
      for (ExDevice.Pin p : d.pins) p.resolved = false;
    }
    do {
      changed = false;
      for (ExDevice d : devices) if (!d.resolved) {
        for (ExDevice.Pin p : d.pins) if (p.isInput) {
          if (!p.resolved) {
            for (ExDevice.Pin cp : p.connected)
            if (!cp.resolved) {
              p.level = cp.level;
              p.resolved = true;
              changed = true;
            }
          }
        }
        int ipins = 0;
        int rpins = 0;
        for (ExDevice.Pin p : d.pins) if (p.isInput) {
          ipins++;
          if (p.resolved) rpins++;
        }
        if (ipins == rpins) {
          update_devices();
          d.resolved = true;
          changed = true;
        }
      }
      dupdates++;
    } while (changed && dupdates < 64);
    

    I added the dupdates flag to prevent getting stuck in a while loop if something goes wrong, but the number of max updates will be increased at the end.

  • Well done!

  • edited May 2018

    I have found out that the algorithm is kind of faulty, because of lines 20-30. Only the one specific device should be updated, but instead I am updating all of them for each device. That means the number of updated devices would increase exponentially with each device added, which could result in terrible performance.

    int dupdates = 0;
    boolean changed;
    for (ExDevice d : devices) {
        d.resolved = false;
        for (ExDevice.Pin p : d.pins) p.resolved = false;
    }
    do {
        changed = false;
        for (ExDevice d : devices)
            if (!d.resolved) {
                d.behavior(); // execute the behavior no matter what because some things are done in an external JS script, even if nothing is connected
                for (ExDevice.Pin p : d.pins)
                    if (p.isInput) {
                        if (!p.resolved) {
                            for (ExDevice.Pin cp : p.connected)
                                if (!cp.resolved) {
                                    p.level = cp.level;
                                    p.resolved = true;
                                    changed = true;
                                }
                        }
                    }
                boolean allires = true; // ik this name is crap but it stands for "ALL Input pins RESolved" (capital letters = letters used in name)
                for (ExDevice.Pin p : d.pins) { // this was the faulty part: the input pins weren't checked for resolving properly
                    if (p.isInput && p.resolved) {
                        allires = false;
                    }
                }
                if (allires) {
                    d.behavior();
                    d.resolved = true;
                    changed = true;
                }
            }
        dupdates++;
    } while (changed && dupdates < 1024);
    
Sign In or Register to comment.