How to construct code that will run on a Grid
Code that will execute on the Grid – Best Practices
• Decomposing code to be efficient on a grid
Possibly the most important aspect of preparing code for execution on the grid is the granularity. If you think of a piece of code it is normal to think of it in distinct sections obviously leading you to factor this into your object design or method decomposition at a finer grained level. Often a choice is made to execute certain parts of the code asynchronously often by a worker thread where synchronization between threads and input and output data is required. This is similar to programming for a grid except there is a possibility that the positive and negative effects of such a programming paradigm can be exaggerated using a grid; i.e. the fast code runs faster (it in fact simply runs more deterministically on a well configured system) and the code that utilizes such things as locks on shared resources could appear to run much worse (as they are potentially holding up or being held up by more waiting processes).
1. Keep code fragments small
2. Minimize blocking and IO
3. If code is naturally sequential choose whether you wish to deploy this as a unit to the grid or break it down and deploy each code fragment to the grid. The advantage of doing this is that one achieves a greater degree of parallelism, but the cost of communication is high therefore if a set of naturally sequential tasks need any of their predecessors for input then this will have to be done either by message passing or shared memory (or both).
4. How independent is it? Loop iteration can be a good candidate, take for example a European option that is to be valued via a Monte Carlo simulation, chances are you have some inputs, you pre-calculate what you can then using a random number generator from a normal distribution inside a loop then perform a calculation and add to a running total; when the loop terminates take an average of the running total and discount it. This could be summarised as follows: -
• Pre-prepare any data prior to loop
• Perform looped calculations
• When it’s completed perform summary calculation(s)
This gives us a good example of a serial piece of code that could take advantage of the grid with few drawbacks. A simple case could therefore be
• Pre-prepare as before
• Perform a set of asynchronous calls to a grid based service that houses the body of the loop
• Register a call-back for completion of the calls, when they have all returned perform summary calculations
Sounds easy. But there are a number of considerations. If there are a large number of nodes in the grid (such that a large proportion of the simulation can be run at once) then this is attractive, if not then it’s much less so because we are obviously introducing network traffic and latency for each calculation call and response (whether we handle the calls synchronously or asynchronously at the client). There are domain considerations such as in this example we would like the random number generator to be seeded such that there are not overlaps.
• Calculating Network round trip time
In deciding how you will break up a piece of code it is often useful to get an idea of the performance constraints of the underlying network. It’s useful to get an idea of how often your code will be called, or how much in terms of resources it consumes vs. how long it will take to execute remotely. You have to consider the cost of serialization as you ship your parameters (at either end) and add network round trip time. Here are a few ideas:-
1. Determine the RTT (round trip time) – do a set of pings periodically from an appropriate machine to a grid broker
2. Perform a tracert on the same as the above to view the number of hops( in a perfect world this is 0 and you can co-exist with multicast pub/sub tools).
3. It may be useful to review the network throughput and the IP settings on all machines and routers/switches (throughput = bandwidth * RTT), check that the NICs and switches are set to full duplex (important if some of the hardware is older). You may wish to review the CWIN and RWIN sizes depending on how much influence you have over the client Network department and which protocol you use.
4. You should note the change in network saturation when you perform a load test after you have done this (Task manager provides a simple view of this but a network monitoring tool is a better bet).
• Where do I get (put) my data?
It is obvious that data is supplied through parameters and a return value can be obtained. In the simplest case this is all you need, however you may need to provide extra data to the service and there are a couple of methods of achieving this: -
1. DataReferences – this is a simple lookup mechanism provided by GridServer and is obtained via a DataReferenceFactory please see p39 of the developers guide. A simple data accessor is passed around which can then be dereferenced – sounds slow to me.
2. Service State – please see Stateful Services section, in short, data can be associated via named methods (when you register the service) that can be used to push and pop values into the engine ‘memory’, this is supported through failover and redistribution to other engine instances.
3. You can just use a Cache – I mentioned that the built in cache tends to be ignored (this is received opinion for me) but there’s nothing stopping you utilising a separate but similarly deployed cache mechanism such as Tangosol Coherence, Gigaspaces etc. The advantage of this approach is you can segregate areas that can be pre populated (maybe from database) and fetch it in the engine initialise phase before the service invocation, so the data is machine/cache local.
-
Recent
-
Links
-
Archives
- February 2008 (3)
- February 2007 (1)
- January 2007 (2)
-
Categories
-
RSS
Entries RSS
Comments RSS