August 14

Dynamic monitor instantiation and reporting via Reflection

We decided to make our WCF client much more dynamic by grabbing all classes that inherit from the PerformanceMonitorBase on the fly, put them in a collection and iterate through them to instantiate, construct, start, and report; all via a feature of .Net architecture called 'Reflection'.

"Reflection provides objects (of type Type) that encapsulate assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, Reflection enables you to access them."

This way, no matter how many performance monitors we build and add to the project, the client code will remain the same:

WcfClient.Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using DistributedIDS.BasePlugins;
using WcfServiceLib;

namespace WcfClient
{
class Program
{
static void Main(string[] args)
{
MessageHost server = new MessageHost();

List perfMons = new List();

foreach (Type pMonBase in Assembly.GetAssembly(typeof (PerformanceMonitorBase)).GetTypes())
{
if (pMonBase.BaseType == typeof(PerformanceMonitorBase))
{
Object o = Activator.CreateInstance(pMonBase);
pMonBase.GetConstructor(Type.EmptyTypes).Invoke(o, new Object[]{});
perfMons.Add(o as PerformanceMonitorBase);
}
}

foreach (PerformanceMonitorBase pMon in perfMons)
{
pMon.StartPlugin(); }

while (true)
{
Message msg = new Message();
foreach (PerformanceMonitorBase pMon in perfMons)
{
msg.PerfMonResult = pMon.GetResult();
msg.Monitor = pMon.GetType();
msg.TimeOfEvent = DateTime.Now;
server.ReportPerformanceResult(msg);
Thread.Sleep(1000);
if (Console.KeyAvailable)
{
Environment.Exit(0); }
}
}
}
}
}
The output:
Result from DistributedIDS.BasePlugins.RamAvailableMonitor on instance : 220
Result from DistributedIDS.BasePlugins.NetUtilizationMonitor on instance MS TCP Loopback interface: 0
Result from DistributedIDS.BasePlugins.TerminalServicesMonitor on instance : 1
Result from DistributedIDS.BasePlugins.DiskSpaceMonitor on instance _Total: 5788 9
Result from DistributedIDS.BasePlugins.ProcessorTimeMonitor on instance _Total: 0
Result from DistributedIDS.BasePlugins.RamAvailableMonitor on instance : 222
Result from DistributedIDS.BasePlugins.NetUtilizationMonitor on instance MS TCP Loopback interface: 8620.932
Result from DistributedIDS.BasePlugins.TerminalServicesMonitor on instance : 1
Result from DistributedIDS.BasePlugins.DiskSpaceMonitor on instance _Total: 5788 9
Result from DistributedIDS.BasePlugins.ProcessorTimeMonitor on instance _Total: 1.393189
Result from DistributedIDS.BasePlugins.RamAvailableMonitor on instance : 222
Result from DistributedIDS.BasePlugins.NetUtilizationMonitor on instance MS TCP Loopback interface: 8618.636
Result from DistributedIDS.BasePlugins.TerminalServicesMonitor on instance : 1
Result from DistributedIDS.BasePlugins.DiskSpaceMonitor on instance _Total: 5788 9
03:22 PM | 0 Comments | Tags: ,

Performance Client and Results

Most of yesterday was spent creating the Performance client which would interact with the Performance Testing service described in the earlier post.
To recap, this service and client was created to make sure that WCF would be able to handle the large amounts of data an enterprise-level network could send.

The code for the Performance Client is as follows:
DateTime start, end;
//PerformanceTestClient is a proxy-class generated to connect to the WCF service.
PerformanceTestClient client = new PerformanceTestClient();
Int32 iterations = Int32.Parse(args[0]);
client.SetRandomValues(iterations);

Console.WriteLine("Testing {0} iterations at {1}", iterations, DateTime.Now.ToString("hh:mm:ss.fff"));
start = DateTime.Now;
for (int i = 0; i < iterations; i++)
{
RandomDataTestValue value = client.GetRandomData(i);
}
end = DateTime.Now;
TimeSpan diff = end - start;
Console.WriteLine("Finished at {0} which took {1} at a rate of {2}/sec", end.ToString("hh:mm:ss.fff"), diff.ToString(), iterations / (diff.TotalMilliseconds / 1000));
Console.ReadLine();

I tested this locally, over the LAN, and over the WAN. The results were as follows:
The first test was done on the laptop.
C:\test>PerformanceClient.exe 100000
Testing 100000 iterations at 02:18:59.749
Finished at 02:19:15.286 which took 00:00:15.5376273 at a rate of 6667.204/sec
Next, I used ngen, which is a tool that compiles the .NET MSIL to machine code for quicker execution.
C:\test>ngen install PerformanceClient.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50727.4918
Copyright (c) Microsoft Corporation. All rights reserved.
Installing assembly C:\test\PerformanceClient.exe
Compiling assembly C:\test\PerformanceClient.exe ...
PerformanceClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

C:\test>PerformanceClient.exe 100000
Testing 100000 iterations at 02:19:48.779
Finished at 02:20:05.269 which took 00:00:16.4892290 at a rate of 6250.489/sec
Unsurprisingly, the results are similar. This is because we test by using a large amount of iterations, so only the first of the iterations is slightly slowed by .NET compiling it in-line.
Next, I changed my laptop's settings to high performance.
C:\test>PerformanceClient.exe 100000
Testing 100000 iterations at 02:21:57.168
Finished at 02:22:12.861 which took 00:00:15.6936276 at a rate of 6667.36/sec
Again, there is no major change.
Next, I tried it on my main workstation.
Testing 1000000 iterations at 09:06:05.044
Finished at 09:07:53.082 which took 00:01:48.0370305 at a rate of 9256.08557891639/sec
The performance is well within the required boundaries for the project. These tests show that when not network bound, the WCF server can process a large amount of requests per second.

Next, we tested LAN
wireless 802.11g lan
Testing 10000 iterations at 03:48:52.124
Finished at 03:49:11.467 which took 00:00:19.3437500 at a rate of 526.6588/sec

wired lan
Testing 10000 iterations at 03:53:55.374
Finished at 03:54:03.420 which took 00:00:08.0312500 at a rate of 1250.031/sec
The results were significantly lower than the localhost test, but they appear to be due to the latency introduced. This is further shown with WAN testing results:
Testing 10000 iterations at 03:21:26.046 Finished at 03:24:43.744 which took 00:03:17.6977803 at a rate of 50.58225734666 99/sec

Testing 10000 iterations at 03:28:23.550 Finished at 03:31:36.605 which took 00:03:13.0546077 at a rate of 51.79881547059 29/sec
These results, while low when compared to localhost's tests, makes sense given the situation. The service request consists of two packets, a request and a reply.
On average, the ping between our two computers was 20ms. The request and the reply must be sent before the next message can be processed.
So, 1000 ms in a second, divided by 20ms per message = 50 msgs/sec, which is our result.
The LAN tests are similar, with the wireless lan transmitting over a busy radio area with about 1-2ms lag, and the LAN performing at under 1ms.
02:03 PM | 0 Comments | Tags: ,
August 11

Performance Testing

In the interest of testing the performance of WCF, I designed a quick testing service. WCF is a collection of communication mediums all configurable through code and configuration files. The purpose of this test was to measure the performance between any of the viable mediums both remotely and locally.

ServiceContract

[ServiceContract]
public interface IPerformanceTest
{
//Generate the provided amount of random values in advance [OperationContract] void SetRandomValues(Int32 amountOfValues); //pull the random value [OperationContract] RandomDataTestValue GetRandomData(Int32 value); }

The class is simple. The only major goal was to minimize time during the test for the preparation of data. That way the time spent to complete the test would mostly reflect the time WCF needed to transport the data.
public class PerformanceTest : IPerformanceTest
{
public PerformanceTest() { DataSet = new List(); } #region IPerformanceTest Members
public void SetRandomValues(int amountOfValues) { RandomDataTestValue data = new RandomDataTestValue(); Random rand = new Random(); //Prepare the random data for the performance test for(int i=0; i < amountOfValues; i++) { data.FractionalValue = rand.NextDouble(); data.Boole = rand.NextDouble() > 0.5; data.Value = rand.Next(); int cnt = rand.Next(1, 10); for (int z = 0; z < cnt; z++) data.Name += (char)rand.Next(32, 126); DataSet.Add(data); } }
public RandomDataTestValue GetRandomData(int value) { return DataSet[value]; } #endregion
public List DataSet { get; set; }
} }
I unfortunately ran into a problem with defining it as an existing service's endpoint, and wasn't able to test it today. Hopefully I'll have that fixed for next time, and be able to post some performance results.
01:14 PM | 0 Comments | Tags: ,
August 10

WCF Basics

Spent most of Friday (08.07.2009) and today getting a basic WCF implementation up and running.

Resources: By following some rudimentary tutorials from the listed resources we were able to create and test a basic WCF implementation.

We have a client/host application running and messaging from client to host on the same machine. Currently the message uses the a Performance Monitor to report current available RAM from the machine.

WcfServiceLib: MessageHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DistributedIDS.SharedLibrary;
using DistributedIDS.BasePlugins;

namespace WcfServiceLib
{
public class MessageHost : IMessageHost { private RamAvailableMonitor ramMon;
public MessageHost() { ramMon = new RamAvailableMonitor(); ramMon.StartPlugin(); }
#region IMessageHost Members
public Message GetAvailableRAM() { msg.PerfMonResult = ramMon.GetResult(); msg.TimeOfEvent = DateTime.Now; return msg;
}
#endregion } }
WcfServer: Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using WcfServiceLib;

namespace WcfServer
{
class Program { static void Main(string[] args) { ServiceHost server = new ServiceHost(typeof(MessageHost)); server.Open(); Console.WriteLine("WCF Server running..."); Console.ReadLine(); } } }
WcfClient: Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using WcfClient.GetAvailableRAM;

namespace WcfClient
{
class Program { static void Main(string[] args) { MessageHostClient client = new MessageHostClient(); for (int i = 0; i < 50; i++) { Message message = client.GetAvailableRAM(); foreach (PerformanceMonitorResult pmr in message.PerfMonResult) { Console.WriteLine("Available RAM: {0}mb", pmr.Result.ToString()); } Thread.Sleep(1000); } } } }
02:58 PM | 0 Comments | Tags:
August 7

Research - Windows Communication Foundation

We are researching Windows Communication Foundation.

Resources: We have started to set up a basic server/client framework and become familiar with the WCF Service Configuration Editor.
03:29 PM | 13 Comments | Tags:
Next → Page 1 of 2