I was just doing some work with the Windows Mobile State and Notifications Broker and came across an interesting performance issue that's more subtle than the performance issues I mentioned on Friday.
In today's case I was writing a simple function that uses the new cellular connectivity state fields to return a string identifying the particular type of cellular connectivity the device currently has. The following is the most likely implementation that one would use.
public string GetCellularConnectionTypeString()
{
string text = "Cellular"; // Prior to WM6 cannot get the below details so just say "cellular"
if (Environment.OSVersion.Version.Major > 5)
{
if (SystemState.CellularSystemConnectedGprs)
text = "GPRS";
else if (SystemState.CellularSystemConnected1xrtt)
text = "1XRTT";
else if (SystemState.CellularSystemConnectedEvdo)
text = "EVDO";
else if (SystemState.CellularSystemConnectedEdge)
text = "EDGE";
else if (SystemState.CellularSystemConnectedUmts)
text = "UMTS";
else if (SystemState.CellularSystemConnectedEvdv)
text = "EVDV";
else if (SystemState.CellularSystemConnectedHsdpa)
text = "HSDPA";
else if (SystemState.CellularSystemConnectedCsd)
text = "CSD";
}
return text;
}
This implementation of the method certainly seems reasonable; we test a separate SystemState property for each cellular connectivity type. The problem is… all 8 of the SystemState properties in the above code are in the same registry location. All of the properties are determined by looking in the key HKEY_LOCAL_MACHINE\System\State\Phone on the named value "Cellular System Connected". Each individual connectivity type is represented by a separate bit in that value. What this means is the above code will go through the overhead of navigating the registry structure, opening the key, extracting the value, and closing the key up to 8 times even though it's reading the same value every time.
A much, much more efficient (orders of magnitude) implementation is to just read the registry value once using the RegistryState class and then apply the individual bitmasks oneself. To do this, I poked around in the <snapi.h> C++ header file that the Windows Mobile 6 SDKs install and found the macro definitions for Cell System Connected values including each of the bit values.
////////////////////////////////////////////////////////////////////////////////
// CellNetwork Connected
// Gets a value indicating what cellular system is used for connection
#define SN_CELLSYSTEMCONNECTED_ROOT HKEY_LOCAL_MACHINE
#define SN_CELLSYSTEMCONNECTED_PATH TEXT("System\\State\\Phone")
#define SN_CELLSYSTEMCONNECTED_VALUE TEXT("Cellular System Connected")
#define SN_CELLSYSTEMCONNECTED_GPRS_BITMASK 1
#define SN_CELLSYSTEMCONNECTED_1XRTT_BITMASK 2
#define SN_CELLSYSTEMCONNECTED_1XEVDO_BITMASK 4
#define SN_CELLSYSTEMCONNECTED_EDGE_BITMASK 8
#define SN_CELLSYSTEMCONNECTED_UMTS_BITMASK 16
#define SN_CELLSYSTEMCONNECTED_EVDV_BITMASK 32
#define SN_CELLSYSTEMCONNECTED_HSDPA_BITMASK 64
#define SN_CELLSYSTEMCONNECTED_CSD_BITMASK 2147483648
I then defined an enum with the same bit values in my C# program.
[Flags]
enum CellSystemConnectedFlags : uint
{
SN_CELLSYSTEMCONNECTED_GPRS_BITMASK = 1,
SN_CELLSYSTEMCONNECTED_1XRTT_BITMASK = 2,
SN_CELLSYSTEMCONNECTED_1XEVDO_BITMASK = 4,
SN_CELLSYSTEMCONNECTED_EDGE_BITMASK = 8,
SN_CELLSYSTEMCONNECTED_UMTS_BITMASK = 16,
SN_CELLSYSTEMCONNECTED_EVDV_BITMASK = 32,
SN_CELLSYSTEMCONNECTED_HSDPA_BITMASK = 64,
SN_CELLSYSTEMCONNECTED_CSD_BITMASK = 2147483648
}
With that, I can rewrite my GetCellularConnectionTypeString method as shown below.
public string GetCellularConnectionTypeString()
{
string text = "Cellular"; // Prior to WM6 cannot get the below details so just say "cellular"
if (Environment.OSVersion.Version.Major > 5)
{
RegistryState cellularSystem =
new RegistryState(@"HKEY_LOCAL_MACHINE\System\State\Phone", "Cellular System Connected");
uint cellSystemConnected = (uint)cellularSystem.CurrentValue;
if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_GPRS_BITMASK) != 0)
text = "GPRS";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_1XRTT_BITMASK) != 0)
text = "1XRTT";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_1XEVDO_BITMASK) != 0)
text = "EVDO";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_EDGE_BITMASK) != 0)
text = "EDGE";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_UMTS_BITMASK) != 0)
text = "UMTS";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_EVDV_BITMASK) != 0)
text = "EVDV";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_HSDPA_BITMASK) != 0)
text = "HSDPA";
else if ((cellSystemConnected & (uint)CellSystemConnectedFlags.SN_CELLSYSTEMCONNECTED_CSD_BITMASK) != 0)
text = "CSD";
}
return text;
}
This code makes exactly one trip into the registry and the rest of the work is simple bitwise-ands which are incredibly cheap operations to perform. As I've said before… the State and Notifications Broker is one of the most powerful and significant features we have in the Windows Mobile platform; however, the managed classes provide a pretty thick abstraction over the real implementation. For the most part this abstraction makes working with the State and Notifications Broker much easier but you always need to be sure you understand the cost of the real operations that the abstraction performs.
Posted
Apr 16 2007, 05:34 PM
by
jim-wilson