Agent Technical Specifications and clock() results

Hello,

I am working on a project that uses the Imp001 as an interface between a Web app and a custom hardware. Currently I am in a study phase and I am performing tests to measure speed and memory.

My first test uses the imp.getmemoryfree() function, and I got two different values if I run the code from the Agent or from the Device. This is the program I used:

/* AGENT SIDE */
/*
Imp Agent memory test 001.
This test uses the function imp.getmemoryfree() that returns the amount of free memory in bytes. 

[Agent] Agent memory at start: 1.046.213
*/

server.log("Agent memory at start "+ imp.getmemoryfree());

/* DEVICE SIDE */
/*
Imp Device memory test 001.
This test uses the function imp.getmemoryfree() that returns the amount of free memory in bytes. 

[Device] Device memory at start	: 81.092
*/

server.log("Device memory at start "+ imp.getmemoryfree());

As mentioned in the listing above this is the result:
Agent Memory: 1.046.213 bytes
Device Memory: 81.092 bytes

From what I know the Device memory is the Imp memory itself, so the program that I write in the device side is stored in the Imp non volatile memory. The Agent memory is the memory of my virtual Imp server running on the cloud. So when/if I turn off my Imp, the device side of the program isn’t lost, when I turn on the device again it just connect to the Agent, that lives permanently in the cloud, is this correct?

Regarding the speed I know I can measure the time elapsed in the device side. I know also I can do the same in the Agent side, but there aren’t similar functions. there are the time() and clock() functions only.

I was working on the clock() function that is supposed to returns the number of seconds elapsed since the start of the process in floating notation. So milli and micro seconds can be easily returned. My Idea is to perform several math calcs and measure how long it takes to accomplish the tasks. Then I can compare the results to the ones returned by other devices that runs the same tasks.

I am concerned by the accuracy of this method. I tried a first test to check if the clock function is accurate:


/*
Imp Agent time test 001.
*/

local StartTime = 0;
local EndTime = 0;
local ElapsedTime = 0;
local Iteration = 0;

function poll() 
    {
    EndTime = clock();
    ElapsedTime = EndTime - StartTime;
    server.log(format("Wakeup elapsed time (%d): %.2fs ", Iteration, ElapsedTime));
    
    StartTime = clock();
    
    Iteration++;
    
    imp.wakeup(3, poll);
    }

StartTime = clock();    
imp.wakeup(3, poll);
 

This test shows the time elapsed every “wakeup”, but the time shown seems not to be correct, in fact this is the dump from the log window:


2014-04-23 11:42:45 UTC-5: [Agent] Wakeup elapsed time (0): 2.19s
2014-04-23 11:42:48 UTC-5: [Agent] Wakeup elapsed time (1): 2.06s
2014-04-23 11:42:51 UTC-5: [Agent] Wakeup elapsed time (2): 1.88s
2014-04-23 11:42:54 UTC-5: [Agent] Wakeup elapsed time (3): 2.25s
2014-04-23 11:42:57 UTC-5: [Agent] Wakeup elapsed time (4): 2.00s
2014-04-23 11:43:00 UTC-5: [Agent] Wakeup elapsed time (5): 2.06s
2014-04-23 11:43:03 UTC-5: [Agent] Wakeup elapsed time (6): 1.94s
2014-04-23 11:43:06 UTC-5: [Agent] Wakeup elapsed time (7): 2.31s
2014-04-23 11:43:09 UTC-5: [Agent] Wakeup elapsed time (8): 1.81s
2014-04-23 11:43:12 UTC-5: [Agent] Wakeup elapsed time (9): 2.06s
2014-04-23 11:43:15 UTC-5: [Agent] Wakeup elapsed time (10): 2.00s

As you can see the time on the left is correct, but the time interval is wrong. I am surprised because I thought to read values greater than 3 seconds. Is something wrong in my way to measure the time interval?

Now the interesting part comes. If I run this code in the device side I get correct results:


2014-04-23 11:46:52 UTC-5: [Device] Wakeup elapsed time (0): 3.00s
2014-04-23 11:46:55 UTC-5: [Device] Wakeup elapsed time (1): 3.00s
2014-04-23 11:46:58 UTC-5: [Device] Wakeup elapsed time (2): 3.00s
2014-04-23 11:47:01 UTC-5: [Device] Wakeup elapsed time (3): 3.00s
2014-04-23 11:47:04 UTC-5: [Device] Wakeup elapsed time (4): 3.00s
2014-04-23 11:47:07 UTC-5: [Device] Wakeup elapsed time (5): 3.00s
2014-04-23 11:47:10 UTC-5: [Device] Wakeup elapsed time (6): 3.00s
2014-04-23 11:47:13 UTC-5: [Device] Wakeup elapsed time (7): 3.00s
2014-04-23 11:47:16 UTC-5: [Device] Wakeup elapsed time (8): 3.00s
2014-04-23 11:47:19 UTC-5: [Device] Wakeup elapsed time (9): 3.00s
2014-04-23 11:47:22 UTC-5: [Device] Wakeup elapsed time (10): 3.00s

So what’s happening exactly? The clock() function returns the time of the device where the code is running on or what? I am a bit confused.

Other than this way, is there another way for me to know the speed of my imp virtual server?

Thanks.

It looks like clock() may not be properly implemented on the agent side… 


Here’s a snippet of code to get precise timings:

(time()%3600)*1.0 + (date().usec/1000000.0);

This will give incorrect results once per hour (since we’re modding the timestamp by 3600)…

It’s important to realize that imp.wakeup() is not designed for precise timings.

imp.wakeup schedules a task (the callback) to be run the next time the Squirrel interpreter is free after the timeout period. In other words, imp.wakeup(3.0, someFunction) tells the Squirrel interpreter to run someFunction as soon as it can (after 3 seconds have elapsed). 

If other things are happening (for example an http.onrequest handler is executing, or a device.on handler is executing, or anything else), the imp.wakeup code won’t finish running until those tasks have completed. 

@Peter might have some more to add to this? 

Yes, the agent runs generally all the time, even when the device is offline.

clock() is not valid on the agent side. If you want timing, use date, which has seconds and usec available in the structure on the agent.

Performance isn’t guaranteed on the agent, as it depends on what other agents are doing, but it’s generally “plenty fast” :slight_smile:

OK, thanks very much.

So I tried to use the formula that you suggested, and I wrote a tiny program that calculates a number in the Fibonacci sequence of numbers and I ran it on Agent and Device sides:


/*
Calculate the time taken to calculate the number of the Fibonacci sequence.
*/

local StartTime = 0;
local EndTime = 0;
local ElapsedTime = 0;
local Iteration = 0;

function fib(n)
{
    if (n < 2) return 1
    return fib(n-2) + fib(n-1) 
}

local n = 25;

// This isn't accurate especially on the Agent side because it's influenced by other Agents tasks
// StartTime = clock();
StartTime = (time()%3600)*1.0 + (date().usec/1000000.0);

server.log(fib(n)+"\
");
// This isn't accurate especially on the Agent side because it's influenced by other Agents tasks
// EndTime = clock();

EndTime = (time()%3600)*1.0 + (date().usec/1000000.0);

ElapsedTime = EndTime - StartTime;
server.log(format("Elapsed time (%d): %.7fs ", Iteration, ElapsedTime));

The Agent side returned these results:


2014-04-23 14:06:41 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:41 UTC-5: [Agent] Elapsed time (0): 0.0874939s
2014-04-23 14:06:46 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:06:46 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:46 UTC-5: [Agent] Elapsed time (0): 0.1030579s
2014-04-23 14:06:49 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:06:49 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:49 UTC-5: [Agent] Elapsed time (0): 0.0831909s
2014-04-23 14:06:51 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:06:52 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:52 UTC-5: [Agent] Elapsed time (0): 0.0936584s
2014-04-23 14:06:54 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:06:54 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:54 UTC-5: [Agent] Elapsed time (0): 0.0880432s
2014-04-23 14:06:56 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:06:56 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:56 UTC-5: [Agent] Elapsed time (0): 0.0919495s
2014-04-23 14:06:58 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:06:58 UTC-5: [Agent] Elapsed time (0): 0.0838623s
2014-04-23 14:07:00 UTC-5: [Status] Device booting; 1.29% program storage used
2014-04-23 14:07:00 UTC-5: [Agent] Fibonacci number: 121393 for 25
2014-04-23 14:07:00 UTC-5: [Agent] Elapsed time (0): 0.0888977s

While the Device side returned these results:


2014-04-23 14:05:08 UTC-5: [Device] Fibonacci number: 121393 for 25
2014-04-23 14:05:08 UTC-5: [Device] Elapsed time (0): 4.0000000s

What it’s interesting is that if you change the value for n in the device side, from 25 to 24 to 23 to 22 you will see that the time elapsed results are: 4, 2, 1 seconds down to 0 for the rest of the numbers of the sequence. The results is always rounded opposite of if you run the same tests on the Agent side.

Anyway, now, I didn’t find the speed of the Device in the datasheet. I presume that, since the SPI clock speed it’s almost 15MHz, the speed of the device should be at least 4x higher but honestly I Googled it but I didn’t find useful information, what is the exact processor inside the Imp?

I would like to go deeper in this because I would like to build a robust time measuring tool for my application.

Thanks.

On the agent, date() has a precision of 1us and an accuracy of probably 1ms, due to using NTP. On the device, date() has a precision of 1s and up to 20ppm drift, due to using a crystal timebase – though it is resynchronised to agent time each time the device connects.

You can time intervals accurately on the device using hardware.micros(), but that again can drift by tens of ppm (independently of date(), which is on a separate crystal).

Peter

The imp001/002 have a clock speed of 120MHz, the imp003 is 168MHz. However, you are running in a VM and other things (like networking) are happening so you don’t get it all to yourself…

Ok, that is very interesting. I hope not to seem picky, but I am trying to understand how “far” I can go with the device and the agent.

From the device side, I didn’t know the speed of the processor (which should be a Cortex M3), so I did a test that toggles a pin, of course I know that Squirrel is an interpreted language, in order to understand at least the speed of the VM.

I discovered I can’t rely on a “bit-bang” way for doing particular things. My GPIO toggler function was:


// DEVICE Side
// assign pin9 to a global variable
_GPIO9 <- hardware.pin9;
// configure LED pin for DIGITAL_OUTPUT
_GPIO9.configure(DIGITAL_OUT);
// set pin to initial value (0 = off, 1 = on)
_GPIO9.write(0);
// function to blink LED
function pinchange() 
    {
    for (;;)
        {
        _GPIO9.write(1);         
        _GPIO9.write(0);
        }
    }
pinchange();  // start the blink function

And what I got on the Oscilloscope was a “stable” square wave with a period of 76us, thus a ~13.15KHz squarewave:

So since the SPI bus can run at high frequencies, for more complicated and precise tasks on GPIOs it would be perfect to use a second MCU.

By the way I worked with an ATSAM3S4BA Cortex M3 mcu by Atmel in the past at a demo project:
CortexM3 VGA Pacman
What is the Cortex M3 MCU inside the IMP, Atmel, NXP ?

Anyway, I hope that this information is helpful :slight_smile:

The very fastest way to bit-bang a GPIO in Squirrel is to “bind” the function to a local variable:

function pinthrash() { local mypin = hardware.pin9; local mypinwriter = mypin.write.bindenv(mypin); for (;;) { mypinwriter(1); mypinwriter(0); } }

but yes, as an interpreted language there’s always going to be tasks whose tolerance for timing is too tight for that. Many such tasks can instead be accomplished using the imp’s fixed-frequency DAC, its sampler, or one of its SPI peripherals (driving WS2812 NeoPixels, for instance) – but for those that can’t, it may be that sometimes a second MCU is the only answer.

The CPU inside the imp001 and imp002 is an STMicro STM32F205 (to save you opening one up and reading that off the top of the chip). We don’t shout about it a lot – partly to give us the leeway to change it in the future if need be, but mostly because if imp users actually need to go and read its 1,300-page datasheet then we’ve failed in our mission to make embedded programming simpler!

Peter

Yes, I know what you mean.

The Imp should be easy enough to program, but I think that it has a lot of potential and there should be a niche that should have the opportunity to dig in more advanced features.

But anyway, I am going to perform a few tests to check how faster is the “binding” way to toggle a pin compared to the other one then I will create a chart to have a visual representation of the speed difference between Agent-Device-PC.

Thanks for the help!

Well, I did several tests with dummy math operations, multiple bubble sort, factorial and I measured the time taken to accomplish the tasks. I can say that the time and clock functions are accurate enough.

Now I have a comparison between PC the Agent and the Device but I am interested in understanding the speed and the memory resource assigned to the agent virtual server. At least I know the best way I can use the agent for my applications.

For example I want to monitor multiple events from a dynamic list, so It’s important to know how the agent handles the resources.

Is there a document that go deep in the agent features?

Thanks.

Not on the agent specifics, no. It has an amount of memory, a variable amount of CPU… it is more like an amazon t1.micro in that it’s very very low cost and hence it has no guaranteed performance.

I’d generally be very surprised if you ran into issues when using the agent for its intended purpose - ie, light data processing and interfacing to web services for imp data.

Well, It seems to be fast in handling common tasks such as connecting to several web services etc.

I did the same math test on the device and on the agent. I run a loop that does a simple math operation on the elements of an array. The programs are really basic and simple and are like this for both agent and device:


for( local i = 0; i < iterations; i++)
     {
     for ( local it = 0; it < items-2; it++ )
      {
          item[it] = item[it] + b * a ;
      }
     }

Where the array items contains 1000 random integers. Basically the formula that calculates the number of operations is this one:

1+ ( iterations * ( 5 + (( items - 2 ) * 7 ))))

I tested different number of iterations and after the tests, these are the average findings:

Electric Imp Agent: 9.26 MHz, ~108 ns each operation.
Electric Imp Device: 110 KHz, ~ 9 us each operation.

Do you think they are reasonable, considering that Squirrel is an interpreted language?

It doesn’t seem unreasonable, but as I said there’s no real definition of “reasonable” here - squirrel performance is not intended to be comparable to bare metal compilation, which is why there’s OS assistance for time critical IO tasks.

Obviously, an AWS server is going to be a lot faster than a cortex-M3 :slight_smile: