Announcement

Collapse
No announcement yet.

Questions about multithreading

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Questions about multithreading

    I would like to multithread my code for maximum efficiency.

    I am thinking about having a thread each to:
    • Send information to the DS
    • Monitor gamepad1 and set motors/servos accordingly
    • Monitor gamepad2 and set motors/servos accordingly


    My questions are:
    • Are the methods for setting power and reading the gamepads thread-safe?
    • Will doing multithreading in this manner cause problems?
    • Will there be runaway threads when the "stop" button is pushed on the DS?

  • #2
    I would highly discourage multi-threading programming. There are two major issues dealing with multi-threading programming:
    - resource contention.
    - threads synchronization.
    It adds even more complication when using the FTC SDK because the documentation does not mention about if any of its APIs are thread safe. And since the FTC SDK changes every year, there would be a high probability it will break something regarding multi-threading than other aspects. If something is not working, debugging multi-threading code is non-trivial. Timing becomes critical, a problem may reproduce during competition but it may not reproduce when you are debugging it.
    There is an alternative to multi-threading programming that will give you the same benefits but not the headache. This alternative is "Cooperative Multi-tasking". Basically, it is using a single thread to multi-task. Since it is a single thread, there is no resource contention and no need to synchronize between threads. The TRC library implemented a cooperative multi-tasking framework. The framework is very similar to a regular OpMode (i.e. init(), loop() etc). When using the framework, you will put your code in the equivalent of init() (i.e. initRobot()) and loop() (i.e. runPeriodic() or runContinuous()). The framework provides objects that register cooperative tasks. For example, the framework provides the FtcGamepad class which registers itself as a cooperative task that monitors all the buttons on the gamepad. If any of the buttons are pressed or released, it will call a notification handler you provided. So it becomes an event driven programming model. To see how this is in action, you can see the gamepadButtonEvent method in our last season's code:
    https://github.com/trc492/Ftc2016Fir...FtcTeleOp.java

    Note that there is a caveat of using the cooperative multitasking framework. As the name suggested, it is cooperative, meaning that unlike preemptive multitasking where your code can be preempted at any random point and therefore the headache of identifying what part of your code must be executed atomically to prevent resource contention. Cooperative multitask does not interrupt your code. Your code is guaranteed to run to completion with respect to other cooperative threads. It is your code who will release the CPU to another cooperative thread when you are done. But this means if your cooperative thread misbehaves and not cooperating nicely, it prevents other cooperative threads from running. Having said that, it sounds complicated but it's not. If you are used to writing code for a regular OpMode, you are already doing what cooperative multitasking requires you to do, namely, do not have any wait loops or sleep statements in your loop() method. Instead, when you need to wait for something to complete its operation, you use a state machine to remember your state and immediately return the CPU to other cooperative threads to run. The next time loop() is called again, you retrieve your state and continue from there. To make all these super easy to use, the TRC framework also implemented a TrcStateMachine class that makes the state transitions event driven. So writing autonomous code becomes implementing code in a linear time event fashion. Again, you can see how simple it is from our last season's code:
    https://github.com/trc492/Ftc2016Fir...utoBeacon.java

    Regarding the three threads you would like to have:

    • Send information to the DS
    • Monitor gamepad1 and set motors/servos accordingly
    • Monitor gamepad2 and set motors/servos accordingly


    Our FtcGamepad class will take care of the 2 and 3. For 1, I assume sending info to the DS means telemetry. If so, you don't need a separate thread, the FTC SDK already took care of it for you. You just call telemetry.addData() method (i.e. fire and forget). If you are talking about constantly updating the DS info regarding the state of the robot, it is just calling telemetry.addData() in your loop(). There is no need to have a thread to do it.

    Comment


    • #3
      To give my two cents, some of which runs contrary to mikets (I will agree on the point that multi-threading is non-trivial).

      My recommendations would be to not use telemetry is a multithreaded manner, and instead use it as "watchdog" on the main thread (the thread that is using your code). If you spin off any thread in your code, you are the one responsible for having that thread stop, or forcing the RC app to crash because of the runaway thread. In effect, what the OP is describing is the basis for how Linear OpModes are accomplished. I could only loosely guarantee that gamepad1 and gamepad2 are thread-safe as that is to be required for the current implementation of LinearOpMode. You can access any HardwareDevice object from any thread you, I just would not have the same thread access the same hardware device.

      If the following is true, you could design your program to be multithreaded:
      • You are not displaying telemetry from other threads than the one your program is given to execute in.
      • Any hardware interactions happen before the child threads are started, and each child thread has "exclusive" access to any Hardware Devices it knows about, i.e. if you spawn a thread to control your drive train, no other threads that you spawn will have access to your drive train (this is also assuming your aren't worrying about reflection attacks).
      • You test your code numerous times, and analyze what you have written to find any bugs.



      Your questions:
      1. For now, yes
      2. Not if you are careful and design your program
      3. You are responsible for stopping your own threads, not FIRST

      Comment


      • #4
        Another vote to use cooperative multitasking instead of multithreading. MikeTs already mentioned the main reasons for that. I am highlighting the fact that most students doing FTC programming lack the experience and insight for the fact that it is not enough that the code works for 99.99 % of time. Debugging of multithreaded code is very complex and difficult - don't do it

        Cheers, Ollie

        Comment


        • #5
          To add to Ollie's highlight, most FTC programmers misuse multi-threading, meaning that they created unnecessary threads where the functionality can be perfectly implemented without a separate thread. In the old NXT Mindstorms days, I have seen too many "tasks" created for no good reasons.
          Just to emphasize the difficulty of writing multi-threaded code correctly, years ago I have found a multi-threading bug in the RobotC code provided by the RoboMatter folks with regarding "joystickdriver.c". In this code, it created a thread (i.e. a task) to periodically copy the joystick structure to a buffer. In the structure, there was a field that contains a bit that indicated if the competition has been "started" (equivalent of pressing the "Start" button on the DS today). Unfortunately, the default state of the bit is 1 and if competition is started, it will be cleared to zero. The code that copies the structure to a buffer first clears the entire buffer, then it copies each fields from the DS structure to the buffer. Unfortunately, the task was preempted right after it "clears the entire buffer" but before it has a chance to copy that field to the buffer. And unfortunately, the waitForStart() task was called and determined the bit was zero so the competition was declared "started" even though it's not. The effect was that our robot started to run even though the competition was not started!!! And of course being a multi-tasking bug, it doesn't always happen, just occasionally.

          Comment


          • #6
            MikeTs, the RoboMatter example is a very relevant to highlight the issues in dormant problems. In late seventies, I did develop a real time operating system that was used in commercial telecommunication and process control applications. Professional developers were using that and other RTOS and I could observe how easy it was them to overlook the synchronization problems that "should never happen".

            Comment


            • #7
              Originally posted by Ollie View Post
              ... I could observe how easy it was them to overlook the synchronization problems that "should never happen".
              And when those "professional" programmers run into that unexplained threading behavior it's not uncommon for them to throw in some mysterious delay code that suddenly fixes the problem. I personally don't see any advantage to using threads in an OpMode unless the Vuforia stuff takes a long time to process. Honestly with a normal OpMode (not linear) you effectively already have the benefit of cooperative multi-tasking. As long as the loop code makes simple quick decisions and exits you'll quickly be back to the next "time slice" through the loop.

              Comment


              • #8
                After re-reading the OP's statement a few times, I now see miket's, Ollie's, and mlwilliam's points.

                4634, what you are trying to is turn something that is already fast, and make it faster for too trivial performance gains, if any. Originally, I thought you were conducting a thought experiment on the concepts of multi-threading, and I encourage you to do what you are trying to do as more of a learning experience than a real world application. I, too, have had to diagnosis multi-threading issues that take way too much time diagnosis for the respective fixes and can attest to the accuracy of some of the other developers you have mentioned issues. I didn't recongize mikets terminology on what he suggests until a realized it was an just another name for non-preemptive multitasking.

                From the Java tutorials on multithreading, "A computer system normally has many active processes and threads. This is true even in systems that only have a single execution core, and thus only have one thread actually executing at any given moment. Processing time for a single core is shared among processes and threads through an OS feature called time slicing." Time slicing may be referred to as quantums in the SDK documentation, and indeed the SDK's idle() and Java Thread.yield() are both suggestive of what happens to the current slice.

                Remember in the grand scheme of things, most concurrency is implemented by time slicing as their is only a finite number of things that can be executed at any given time. Consider the ridiculous case where you are writing a program that displays 1-1000 (doesn't have to be in order for the sake of argument)? Which is going to be faster on a system with N cores: a program that creates 10N threads to display each unique number, a program that creates N threads to display each unique number, or a program with just one thread that displays each unique number? From my testing, the last case is actually the fastest (screen IO is actually thread-safe in Java, so each operation may be out of order, but their won't be intermingling of print statements). However, I want to point out that the first case by far had worse performance than the others. This is a case where multi-threading isn't appropriate for the task, just as a fair amount of FTC robotics applications aren't suited to being multi-threaded. There is not much need to use parallelism in most team's code, however there are use cases like vision processing.

                Robotics libraries like Xtensible and TRC both have multitasking features, TRC is probably better due to more testing, loads more examples, having less yet to be undiscovered bugs. However, Xtensible includes out-of-box support for multithreading via @Async (and cool things like dependency-injected OpModes, and replaying actions of a gamepad).

                Comment


                • #9
                  Originally posted by mlwilliams View Post
                  And when those "professional" programmers run into that unexplained threading behavior it's not uncommon for them to throw in some mysterious delay code that suddenly fixes the problem.
                  Well, I don't consider that a "professional way" of fixing bugs. At the place I used to work, every fix has to be code reviewed and explained why it happened and how it was fixed. Mysterious delay code won't pass our code review process.

                  Comment


                  • #10
                    Originally posted by dmssargent View Post

                    4634, what you are trying to is turn something that is already fast, and make it faster for too trivial performance gains, if any.

                    From the Java tutorials on multithreading, "A computer system normally has many active processes and threads. This is true even in systems that only have a single execution core, and thus only have one thread actually executing at any given moment. Processing time for a single core is shared among processes and threads through an OS feature called time slicing."

                    1) We have a very complex motor curve scaling algorithm, so I do think that parallelization would improve performance.

                    2) The ZTE phone have a quad-core CPU. If they only had a single-core CPU, then I'd completely agree with you that more threads wouldn't help. But with four cores, it can be running four threads simultaneously.

                    Comment


                    • #11
                      I agree completely. That's why I put professional in quotes. I currently work on a product with 5+ million lines of source and every commit is code reviewed.

                      Comment


                      • #12
                        Originally posted by 4634 Programmer View Post
                        1) We have a very complex motor curve scaling algorithm, so I do think that parallelization would improve performance.

                        2) The ZTE phone have a quad-core CPU. If they only had a single-core CPU, then I'd completely agree with you that more threads wouldn't help. But with four cores, it can be running four threads simultaneously.
                        1) How long does it usually take to compute the motor scaling? How often do the gamepad's get updated? (I can't assume you are using stock SDK)
                        2) I agree with both your and my points. The ZTE isn't a single core device, so it can run more things at once. My point was to demonstrate that you shouldn't be creating unnecessary threads for tasks that parallelism does nothing for. Your OP lead me to assume simplest cases, i.e. tank drive, which could not be efficiently modeled to be a parallel program.

                        Everyone's point here is that it seems to be unnecessary for you execute your originally posted design as the reward to risk ratio was not your favor.

                        Comment


                        • #13
                          Originally posted by 4634 Programmer View Post
                          1) We have a very complex motor curve scaling algorithm, so I do think that parallelization would improve performance.

                          2) The ZTE phone have a quad-core CPU. If they only had a single-core CPU, then I'd completely agree with you that more threads wouldn't help. But with four cores, it can be running four threads simultaneously.
                          No matter how complex your motor curve is, you are still bound by the motor update rate of the FTC SDK. Like you said, the phones today are multi-core. But even with single core, they can easily out perform the NXT Mindstorms. So they are not slow. With robotics control applications, they are mostly I/O bound. The only exception is vision. So if you take vision out, there is no good reason to use more than one thread. That's why in the TRC library for FRC, we only have a single thread doing cooperative multitasking but we have a second thread doing vision.
                          You can easily add some profiling code to determine if you are CPU bound or I/O bound. If you are using regular OpMode (or even LinearOpMode you just treat the while (opModeIsActive()) is your loop() method), just make sure you don't have any wait loop or sleep statements in your loop() method, record a timestamp at the beginning of the loop and at the end of the loop. Print out the elapsed time. That would be how much time you are spending in one iteration. As long as you don't have code busy waiting for something, it would be a true representation of how much work you are doing in one iteration of the loop. If the elapsed time is less than the DS update rate, then you are I/O bound. Now be careful how you interpret the elapsed time, your OpMode (or LinearOpMode) loop is still a thread in a preemptive multi-task OS, so you could occasionally be preempted in the middle of your loop() method. So unless you add code to play with thread priority and critical section, your elapsed time of one iteration may be longer than it actually is. For a simple experiment, I usually calculate the min/max/average elapsed time of multiple iterations, print them out and you know that the min elapsed time is most probably the real elapsed time.

                          Comment


                          • #14
                            Originally posted by dmssargent View Post
                            1) How long does it usually take to compute the motor scaling? How often do the gamepad's get updated? (I can't assume you are using stock SDK)
                            2) I agree with both your and my points. The ZTE isn't a single core device, so it can run more things at once. My point was to demonstrate that you shouldn't be creating unnecessary threads for tasks that parallelism does nothing for. Your OP lead me to assume simplest cases, i.e. tank drive, which could not be efficiently modeled to be a parallel program.

                            Everyone's point here is that it seems to be unnecessary for you execute your originally posted design as the reward to risk ratio was not your favor.
                            BTW, two cores do not necessarily run twice as fast. Multi-core architecture is still bound by shared memory/cache/IO etc. It benefits most if you are running different programs because these programs do not interact with each other and less likely to access shared resources. But for the same program with mutli-threads, they interact with each other and at times, they need to synchronize with each other for shared resource accesses. Therefore, you will find most threads are really sleeping waiting for another thread. This is especially true for I/O bound applications such as robot control.

                            Comment


                            • #15
                              And BTW, even if you have a CPU bound situation, multi-threading still can't help you unless the CPU bound problem can be split into multiple smaller problems so that each smaller problem can be assigned a separate thread. If the CPU bound problem is solving something that is serial in nature, then even if you have a thousand cores, it won't help you at all. One of my post grad thesis was about how to partition a complex problem to be solved by a massive parallel processor network. The problem must be partitioned to minimize interactions between each of the sub-problems. If the problem is not partitioned right, then each sub-problem will be waiting for information from another sub-problem.
                              Therefore, I am just wondering what kind of complex motor curve can be efficiently computed by multiple threads and why does it take so much time to compute? A lot of times, even computational intensive code can be simplified with clever algorithms (e.g. N-square algorithm versus log(N)).

                              Comment

                              Working...
                              X