Profiler

Video Transcript

The purpose of the Profiler tab is to help you understand how much of the available processor time will be used when your program is running on an Arduino. It takes time to execute the code that is encapsulated by a node. Embrio can record exactly how long the nodes in your project take to execute and can use this information to estimate the total time used by the processor. By default all Agents in a project execute 32 times per second.

There are 1000 milliseconds in a second, which means that by default one full loop of an Embrio program has 31.25 milliseconds to execute all of the nodes in the project, and this full program loop executes 32 times per second. If the nodes in your project take longer than this then your nodes will in reality be executing at a rate slower than their target refresh rate. Note that this isn’t necessarily a bad thing and will go unnoticed for a lot of programs, but for cases when a consistent refresh rate is important the Profiler is a necessary tool.

The Compile Timeline tab on the Profiler has a visual representation of the execution plan for your program. This is the timeline for a simple program that uses a potentiometer to control which of 4 states of an LED is active: Off, Blink, Fade, and Step. The Total Milliseconds value here is 31.25 seconds because all Agents in the program are updated 32 times per second. Of that 31.25 milliseconds, only 1.46 milliseconds are used by this program. The graphic on the left shows you that out of the 1000 milliseconds in a second, this program will only be using the processor for 46.62 milliseconds, or about 5% of the time. The rest of the time the processor will be in a “delay” state waiting to execute the next node.

Each colored line on the timeline represents the time reserved for one node instance. Not all code in a node fires on every update, some code only runs when an input changes, and some only runs when a trigger fires. Embrio still has to reserve the maximum time this code will take to execute to keep a consistent refresh rate. You can click on these lines to see which node on which agent the reservation is for, and exactly how many milliseconds this node takes to execute on each update, how many updates the node has per second, and the total milliseconds per second for this node. It also shows the total number of updates per second for all nodes of this type, and the total milliseconds per second for all nodes of this type. For example his program has 3 Above or Below nodes, each one updates 32 times per second, for a total of 96 updates per second.

If Embrio doesn’t have timing data for a node, the node shows up on the timeline as an empty place holder. To get timing data for all untimed nodes in the project, either press the clock icon in the Arduino connection control in the top right of the screen, or press the Run timing on Arduino button on the Profiler. Embrio will generate a program that executes the code in each node several times and averages the execution time, and sends that data back to the computer.

By default, the “all nodes must have timing data to upload” option is not checked. This means that you can compile and upload your finished program even without timing data. With this option checked, before you can upload a final program, Embrio needs to get timing data for all nodes. Note that if this option is not checked, the time between executions of your nodes can vary depending on how long it takes for the rest of the nodes to execute. When there is no timing data, on every loop of the program, each block of code will execute and then move on to the next block of code without waiting. When there is timing data, a block of code is executed, and then the program pauses with a “delay” call for any unused reserved time. With this delay, the amount of time between executions of an instance of a node is very stable, so if a node is set to update 32 times per second, the code in that node will execute very close to every 31.25 milliseconds. So if accurate timing is important to your program, make sure you have this option turned on.

The other two tabs on the Profiler control show the timing data in other useful ways. The Agents tab lists all of the Agents in your project, along with how many nodes are on that agent, the milliseconds per update needed to execute all of those nodes, the number of updates per second, the total used milliseconds per second used by all the nodes, and how many nodes on the Agent have no timing data. You can sort by any column to help track down which Agents are using the most resources. This can help you decide what refresh rate your agents should run at. If an Agent is taking a long time to execute, you might want to set it to run at a slower refresh rate, depending on the function of the Agent.

The Nodes tab shows all node definitions used in the project, as well as how many instances of that node there are, the number of milliseconds per update of that node, the total number of updates per seconds for all instances of that node, and the total milliseconds per second used by that node type. Again this data can be sorted to help find the most expensive nodes. When you select a row, there is also a sub table that shows all instances of that node type. Double clicking on a row will open the Agent and select the instance of the node.

By default, all Agents execute 32 times per second, but they could execute at different rates. For example, we might want the LED Agent to execute 64 times per second for a smoother result. When the refresh rate is changed, the Compile Timeline changes. Now the full 31.25 millisecond program loop is broken into two sections of 15.62 milliseconds each. The nodes that refresh 32 times per second are spread out into one of the two groups, while the nodes that execute 64 times per second are put in both sections. You can play around with changing different Agent’s refresh rates to see what effect this has on the program execution timeline.