Fez Cobra III Analog Input read rates

In other blog posts I have measured the AnalogInput read rate of my Netduino, FEZ Lemur and FEZ Panda III devices and was surprised by some of the numbers. Now, I have another project which uses a GHI Electronics FEZ Covbra III so have done another quick test.

This is just a simple test, not terribly representative of real world I just wanted to get comparable numbers.

public static void Main()
{
   int value;
   AnalogInput x1 = new AnalogInput(FEZLemur.AnalogInput.D19);
   Stopwatch stopwatch = Stopwatch.StartNew();

   Debug.Print("Starting");

   stopwatch.Start();
   for (int i = 0; i <; SampleCount; i++)
   {
      value = x1.ReadRaw();
   }
   stopwatch.Stop();

   Debug.Print("Duration = " + stopwatch.ElapsedMilliseconds.ToString() + " mSec " + (SampleCount * 1000 / stopwatch.ElapsedMilliseconds).ToString() + "/sec";);
}

Fez CobraIII 120 MHz CPU
Duration = 9297 mSec 10756/sec
Duration = 9297 mSec 10756/sec
Duration = 9298 mSec 10755/sec
Duration = 9296 mSec 10757/sec
Duration = 9298 mSec 10755/sec

Something is not quite right here need to look at my code and the numbers some more.

Fez Lemur & Panda III AnalogInput read rates

I had previously have measured the AnalogInput read rate of my Netduino devices and was surprised by some of the numbers. Now, I have another project in the planning phase which will be using a GHI Electronics Fez Lemur or Fez Panda III device and had time for a quick test.

This is just a simple test, not terribly representative of real world just to get comparable numbers.

public static void Main()
{
   int value;
   AnalogInput x1 = new AnalogInput(FEZLemur.AnalogInput.A0);
   Stopwatch stopwatch = Stopwatch.StartNew();

   Debug.Print("Starting");

   stopwatch.Start();
   for (int i = 0; i < SampleCount; i++)
   {
      value = x1.ReadRaw();
   }
   stopwatch.Stop();

   Debug.Print("Duration = " + stopwatch.ElapsedMilliseconds.ToString() + " mSec " + (SampleCount * 1000 / stopwatch.ElapsedMilliseconds).ToString() + "/sec");
}

Fez Lemur 84 MHz CPU
Duration = 2855 mSec 35026/sec
Duration = 2854 mSec 35038/sec
Duration = 2854 mSec 35038/sec
Duration = 2854 mSec 35038/sec
Duration = 2861 mSec 34952/sec

Duration = 2856 mSec 35014/sec
Duration = 2854 mSec 35038/sec
Duration = 2855 mSec 35026/sec
Duration = 2854 mSec 35038/sec
Duration = 2854 mSec 35038/sec

Fez Panda III 180MHz CPU
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec

Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec
Duration = 1799 mSec 55586/sec

It looks like the GHI Team have a performant implementation of AnalogInput.ReadRaw()

Netduino 3 AnalogInput read rates

At CodeClub some of the students build a power consumption meter and as part of that project we measure the AnalogInput sample rates to check they are sufficient for our application.

Earlier this term when we measured the sampling rates in a CodeClub session we had a mix of Netduino 2 and Netduino 3 devices and some of the results differed from my previous observations. I used the same code on all the devices

int value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();
}
stopwatch.Stop();

Netduino Plus 2

Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec

Netduino 3

Duration = 2071 mSec 48285/sec
Duration = 2069 mSec 48332/sec
Duration = 2070 mSec 48309/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec
Duration = 2070 mSec 48309/sec
Duration = 2070 mSec 48309/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec

Netduino 3 Ethernet
Duration = 2136 mSec 46816/sec
Duration = 2137 mSec 46794/sec
Duration = 2136 mSec 46816/sec
Duration = 2135 mSec 46838/sec
Duration = 2135 mSec 46838/sec
Duration = 2137 mSec 46794/sec
Duration = 2137 mSec 46794/sec
Duration = 2135 mSec 46838/sec
Duration = 2136 mSec 46816/sec
Duration = 2135 mSec 46838/sec

Netduino 3 Wifii
Duration = 3902 mSec 25627/sec
Duration = 3901 mSec 25634/sec
Duration = 3902 mSec 25627/sec
Duration = 3902 mSec 25627/sec
Duration = 3901 mSec 25634/sec
Duration = 3903 mSec 25621/sec
Duration = 3903 mSec 25621/sec
Duration = 3902 mSec 25627/sec
Duration = 3902 mSec 25627/sec
Duration = 3903 mSec 25621/sec

The results for the Netduino 3 & Netduino 3 Ethernet were comparable with the Netduino Plus 2 in my earlier post. The reduction in the sampling rate of the Netduino 3 Wifi warrants some further investigation.

Netduino AnalogInput read rates

At CodeClub some of the students are building a netduino based power consumption monitor using an Energy Monitor Shield (with my modifications). The approach for the software was “inspired” by the Arduino code developed by the Open Energy Monitor project. First step was to confirm that the Netduino 2 Plus we were using could sample the Alternating Current(AC) waveform often enough.

The first version of the code called the Microsoft.Spot.Hardware.AnalogInput.Read method (which returns a floating point number) 100,000 times. The duration was measured using a Stopwatch class written by ChrisW of Secretlabs.

double value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.Read();
}
stopwatch.Stop();

Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec

The AnalogPort also has a overloaded Read method which returns an integer

int value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();
}
stopwatch.Stop();

Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec

There is also a Secret labs AnalogInput which has a Read method which returns an integer

int value;
SecretLabs.NETMF.Hardware.AnalogInput x1 = new SecretLabs.NETMF.Hardware.AnalogInput(Pins.GPIO_PIN_A0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.Read();
}
stopwatch.Stop();

Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8564 mSec 11676/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec

The int Microsoft.Spot.Hardware.AnalogInput.ReadRaw() appears to be  quite a bit faster than the other two approaches.

SQL Azure Database performance oddness

For a few years I have worked on a midsize Azure application for managing promotional vouchers which are delivered via SMS or email and redeemed via software integrated into the point of sale terminal software of several local vendors.

A promotion has a batch of vouchers which are available for allocation to consumers and I had noticed from the logs and performance counters that the process of getting the next voucher slowed down significantly as the number of available vouchers decreased.

Voucher batches range in size from 100’s to 100,000’s of vouchers and the performance of a small promotion for a local wine shop was where I initially noticed how much the duration increased. The promotion had roughly 850 vouchers the allocation of the first vouchers took 10’s of mSec each but the last 10 vouchers sometimes took more than 1000mSec each.

I took a copy of the live database and removed the customer data so I could explore the performance of my TSQL in a controlled environment. I initially downloaded a copy of the database to one of my development servers and tried to simulate the problem while monitoring performance using SQL Profiler and other tools but the allocation time was fast and consistent.

The database performance appeared to be a SQL Azure specific issue so I built a cut back web role test harness which called the underlying stored procedure so I could closely monitor the performance. The test harness could make a specified number of calls recording the duration of each call and the overall duration. I then de allocated all the vouchers in the wine shop promotion and allocated them in chunks(all durations are in mSec and are the average time it takes to make a single call)

100 vouchers at a time (0 – 800 of 843 vouchers)

43, 88, 136, 191, 260, 305, 358, 379

10 vouchers at a time (800-840 of 843 vouchers)

431, 440, 404, 412

1 voucher at a time (840-843 of 843 vouchers, last one is failure)

400, 423,404, 390

After some debugging, progressive removal of code and looking at query plans I identified the problematic TSQL statement.

UPDATE TOP(1) Voucher SET
Voucher.AllocatedByActivity = @ActivityUID,
Voucher.AllocatedAtUTC = @RequestDateTimeUTC,
Voucher.AllocationReference = @ClientReference,
@VoucherCode = Voucher.Code
WHERE (( Voucher.VoucherBatchUID = @VoucherBatchUID ) AND ( Voucher.AllocatedByActivity IS NULL ))

This update statement uses a single statement transaction to get the next random un-allocated voucher code in the specified voucher batch.

After some conversations with a SQL Azure support engineer at Microsoft (who was very helpful) he figured out that in SQL Azure the query processor needed a query hint to tell it to use an existing index to make it perform consistently.

UPDATE TOP(1) Voucher SET
Voucher.AllocatedByActivity = @ActivityUID,
Voucher.AllocatedAtUTC = @RequestDateTimeUTC,
Voucher.AllocationReference = @ClientReference,
@VoucherCode = Voucher.Code
FROM Voucher WITH (INDEX(IX_VoucherByVoucherBatchUIDAllocatedByActivity))
WHERE (( Voucher.VoucherBatchUID = @VoucherBatchUID ) AND ( Voucher.AllocatedByActivity IS NULL ))

100 vouchers at a time (0 – 800 of 843 vouchers)

20, 8, 13, 9, 32, 7, 15, 9

10 vouchers at a time (800-840 of 843 vouchers)

17, 9, 14, 13

1 voucher at a time (840-843 of 843 vouchers, last one is failure)

12,11,13, 5

I always have plenty of performance counters and logging (using the enterprise library) on my Azure web and worker roles but I was also lucky that I noticed something odd in the logs while checking on the progress of another promotion. I actively monitor the performance of my Azure applications as over time the performance characteristics of the underlying hardware will change as fixes and enhancements are released.

Netduino Plus 2 Interrupt Performance

I had been considering using the Quadcopter IMU to drive the execution of the orientation and stabilisation algorithms. The MPU 6050 has an interrupt output which can be configured to trigger when there is data available to be read etc.

I had read discussions about the maximum frequency which the Netduino & the NetMF could cope with and just wanted to check for myself. For this test I assumed that the PWM outputs (once initialised) consume little or no CPU and that with only an InterlockedIncrement in the interrupt handler that these would be the maximum possible values.

Netduino Plus 2 Interrupt Testing

I used the onboard PWM (D3) to drive an interrupt (D1) and then had a background thread display the number calls to the interrupt handler in the last second. The button (D4) in the picture above allowed me to increase the frequency of the PWM output in 100Hz steps. The desired count and actual count where displayed using Debug.Print and the .Net Microframework deployment tool

100 101
200 107
700 453
1000 867
1500 1225
1800 1629
2100 1943
2900 2470
3200 3093
3600 3432
4000 3799
4000 4020
4000 4021
4000 4020

The counts seemed to be reasonably accurate until roughly 13KHz then there would be major memory allocation issues and the device would crash.