Utilizing Both RoboRIO Cores

I keep hearing about how the RoboRIO has two cores, and you can put things like all your standard robot functions on one core while putting things like vision and image processing on another core. How do we do this? Do tasks get automatically delegated to a specific core, or do we have to specify which core to use in code? I honestly have no clue about this, so any help is appreciated. We’re using java, but knowing how to do it in LabVIEW would be nice for future reference as well.

Under standard programming procedures, the operating system will automatically load balance between the two cores. You need not do anything.

Under labview, you have the option using (among other things, timed loops) to allocate processes to specific cores. Standard Labview programming is to use while loops which would be allocated automatically to balance.

Actually, the operating system will not balance any program on two cores if you are not using multiple threads, which you should hard code.

Although that if you’re talking about Java/C++ WPILib specifically, I think that every command is ran in a different thread, and you also have the “task” class in WPILib which offers a more convenient interface to operate threads.

The operating system might not, but the LabVIEW compiler will automatically handle thread allocation and parallel execution on code paths that execute simultaneously.

1 Like

Can you provide a more detailed explanation of how this works to allocate threads? Just curious.

There’s a lot going on, but here’s a white paper on it which might help explain the approach: http://www.ni.com/white-paper/14565/en/

We have any actual operating system, not a glorified boot loader. By default, we’re running multiple threads. At least one is our program, the rest is the Linux kernel, a Web Server, and anything else running on the RoboRIO.

And as you yourself just stated, multiple threads are automatically built into WPILib. As crake noted, in LabView multithreading is automatically performed by the compiler without need for programmer declaration.

Backing way up to the original question, paraphrased as – How can my team’s code take advantage of a multi-core target?

First off, this is no different than any other modern OS and computer processor. So if you know how to do this on Windows or linux desktop, you know how to do it on your roboRIO. And once you learn to do it on the roboRIO, you will know how to do it elsewhere.

If you do nothing in your code, or intentionally write your code to run in a single thread, the OS will run other processes’ code simultaneously with your code. So the cores will both get some use, but nowhere near optimal. Your code will only ever run on one of the cores at a time.

In order for your code to run on more than one processor, the process executing your code needs to expose threads of execution to the OS. Within your process, you allocate this finer grained, lighter weight, execution concept called a thread. The OS scheduler can then schedule the pool of threads contributed from various processes and can utilize both cores.

For C++ and Java, you can use tasks or allocate threads. I do not think that commands currently use more than one thread. PID subsystems do, however. So WPILib code in these languages will automatically get some use from multiple cores. As with PID, WPILib can do some things automatically, and that will likely happen over time. Other libraries such as OpenCV may also internally use threads to use multiple cores.

Now for LabVIEW:
The runtime of LabVIEW automatically generates a pool of threads of varying priorities and sub-schedules the code with these threads. The white paper covers some of the details. I’ll try to summarize the implementation below, but the upside is that the compiler and runtime take advantage of parallel expressions automatically. LV doesn’t expose threads as a concept because data flow languages can do this quite well without it. Independent code executes on as many cores as you have. If you want to know how, read the white paper or the details sections below.

LabVIEW Implementation Details:
LabVIEW is a compiled language. Whether deployed or running in the debugger, the VI is always compiled first. A VI resembles a function, but in many respects, it is more. The VI is analyzed for asynchronous operations and parallel operations. Nontrivial parallel operations are compiled into what we call “clumps”. As an example, if a VI branches the parameters into two or three parallel loops that independently compute values on the inputs, the compiler will typically generate two or three independent clumps, one for each asynchronous loop. The VI function internally has smaller elements – clumps – that it schedules as it executes.

When a VI begins execution, one or more clumps are scheduled for execution, and as they complete, they schedule downstream clumps that depend on their output data. This continues until the system is idle. Since clumps can call other VIs, which expose more clumps, the system is often executing loops or loop contents from numerous VIs.

The default template for LabVIEW has a periodic tasks VI with parallel loops that wake up on a timer, do work, and go back to sleep. Each loop is its own clump. The vision VI is similarly its own loop/clump. The Network Tables Server – another collection of parallel clumps. Start Communication is another. So by default, there are probably about a dozen clumps sharing the two cores – they also take naps.

By the way, this is also how LV VIs execute on the cRIO. Of course with only one core, only one clump can actually be running at a time, but there are still benefits, as timed and I/O operations can wait in parallel with someone else’s execution.

There is a tool called the execution trace toolkit that will show all active clumps over time, and will show how they interrupt each other based on priority and other mechanisms. It produces a ton of data to look at, but it is geeky fun to be able to see into the runtime and see how it executed your code at such a fine level of detail.

Feel free to ask other questions.
Greg McKaskle

That is correct. Commands do NOT use multiple threads. The scheduler calls the execute method in a command once about every 20 ms. PIDController does spin up a thread to handle updating the motor, which also loops every 20 ms.

What’s the best method to look at that data? I’m not sitting in front of a workstation with students at the moment but I will be later tonight. Is that tool part of the normal install? Does it generate output in a graphical way that is easy to digest? Are there any tutorials for how to use it?

Asking a clarifying question on that statement:
Commands may not use multiple threads - but in the context of WPILib and the OS itself, there could be multiple commands running at the same time - and I’m assuming that those can be scheduled by the OS to target different cores?

Is there an easy way for teams to go in and see how much throughput each core is using of its potential maximum capacity?

The command based structures do not use multiple threads to run multiple commands. There is one thread running all of the commands.

I believe the tool is there, but it is hard to tell for certain since I have lots of other stuff installed on my computer.

The Tool itself is located under Tools>>Real-Time Module>>Trace Viewer. That will lead you to documentation for how to generate a trace.

When I wrote the other post, I was combining some features from the desktop and the RT version of this tool.

Anyway, to use it with LV, it requires you to add two VIs. They are located in the Real-Time>>RT Execution Trace Tool palette. A palette search of TraceTool may be the easiest way to get there.

Wherever you want to start the trace, place the TraceTool Start Trace.vi. I dropped it to the left of Begin VI and wired the error out of from Start into Begin. Next, decide where to stop the trace. I clicked through the Mode Case to the Finish frame and added the TraceTool Stop Trace and Send.vi. I knew my host IP address, so wired that up as 172.22.11.1 since I was using the USB cable. I don’t know if it is necessary, but made sure this finished before the Finish VI was called by using a sequence structure.

I then opened the Trace Tool, ran the robot code using the run button, let it run a bit and clicked finish. The attached image shows the table with thread activity above and VI activity below. The tools in the upper right let you zoom in and out.

The VI section shows control packet arrivals every 20ms, with little slivers of VI activity that result, then a nap until the next one arrives. If you zoom and scroll, you can find the periodic task loops going off on their period.

You can further instrument code using the other VI in the palette to log a user event. As you can see, there is a LOT of data generated. You may want to do acquire of these in advance and then open them with the students. This is a very advanced tool, a microscope for finding needles in haystacks. It will not trace VIs that have already started execution when Start is called. It can sometimes cause as many questions as it answers.

Have fun with it.
Greg McKaskle





I can’t seem to find the task class in my WPILib API. Could it be under a different name? Also, how exactly would I create a new thread and populate it with instructions/commands? Sorry, I’m entirely new to this concept and pretty much have no clue what I’m doing.

Perhaps from here we can talk about things that should and shouldn’t be separate threads. For example, is there any value in putting different mechanisms on different threads or does that not matter? Vision is the obvious choice for a new thread, yet then where does the thread safety come into play when translating the results of vision to what the robot actually does?

For example, in teleop 2013 this example logic would simply allow vision to override what the driver did without a state, supposing that vision was processed later in the loop than control inputs. Yet in 2015 using threads, the output ports would be set to two different values based upon two separate PID targets. Neither PID target would ever be reached - what is the behavior of the robot?


Thread A: Button Input -> Input Processing -> Use PID to go to preset since button is held
Thread B: Vision Processing -> Use PID to go to vision angle until operator cancels

Sure, once we discover all of these errant scenarios we can prevent them via operator behavior modification, but until then I’m sure we’d like to not smoke the motors.

I’m on my laptop and I don’t have the 2015 WPILib there, but in 2014’s there’s a task class.

It uses the VxWorks TaskLib (which is probably POSIX compatible). I think that thhis year they use POSIX threads since the RoboRIO runs Linux (with RT extensions).

The vxWorks taskLib is the native interface for creating and manipulating new tasks - it is not POSIX. But VxWorks has a POSIX compatible library if it was included - I do not remember if it was or not.

The POSIX call to create a new thread is pthread_create and you can associate the thread with a particular core using sched_setaffinity (after the thread is running) or with pthread_setaffinity_np (to setup thread attributes before the thread is started).

Whether you should or not depends on the toplogy of your application. Remember that switching from one thread to another running on a different core can be as time consuming as switching between threads that are part of different processes.