Dynamic navigation for higher performance

Reducing or removing navigation code from the GUI by adding derived links and, at the same time, improving performance.

Modeling class structures takes some thinking, and when done the thinking and the drawing and after that starting up using the model, then you'll spend awful lots of code traversing links in order to retrieve trivial info in a given object structure. Navigating the same somtimes complicated link-paths over and over again takes CPU power, and it also takes much redundant code and/or expressions accessing the same navigation paths over and over again. In the Bold architecture you would also place redundant subscriptions from many different locations to the same target subscribing to the same paths, over an over again.

But with Bold you can often directly address unnecessary navigation, the redundant navigational code/ocl-expressions and gain decrease of CPU load by adding derived associations instead making the few persistent links more useful and "intelligent". And furthermore, doing this directly in your model your model will at the same time reflect more specifically which parts of the model is actually used. And how.

An example can show a typical situation where adding many derived links, extending the model with more useful info, will result in more efficient, less redundant and more readable code. If we were talking traditional handcrafting you probably would have thought I was joking, because we usually don't save us from more problems by adding more relations between objects...! But I'm not joking - you will actually achieve all the mentioned benefits  by adding a few simple lines to the model using Bold.

EXAMPLE

A truck vehicle combination can serve us with an example. The example model will consist of Vehicle units, some Parcels and a trip object keeping all the trip info and calculations. With these three basic categories we will end up writing almost 50% of code (or expressions) in algorithms navigating the basic structure which has only three persistent links... Such coding is trivial, takes time and adds nothing of value to the system.

First a "RIL world representation" of the example before representing it using an UML class diagram ("RIL" is my initials) :

Fig 1: This Vehicle combination contains of Parcels, but only the front most VehicleUnit keeps Trip info about distances, events and economic calculations.

 1.jpg

Disregard the arts aspects of the illustration. For several reasons, not discussed here, the VehicleUnits shown above always has a trip object attached, but only the front most trip object is representing the entire combination. The other trip objects/calculators are "demobilized" until the trailer (possibly) is disconnected from the truck, and then the trailer will be a valid ("front most") TripHolder on its own.

Imagine you are viewing one of the Vehicle units or one of the Parcels in a UI and you would probably like to know more about trip related info, perhaps distances traveled, addresses visited, the visting order, costs or revenues per km etc (all administered by the trip object).

There are only three relations in this model but still you would have to think twice when trying to navigate correctly trying to find the "calculator" (here representing a "trip class").

It would be even worse if you want to implement some generic logic or calculations for the VehicleUnits because you would always have to make sure that you navigate to the "front most vehicle unit" (lets call it the "hauler") and from there navigate directly to the trip info (the calculator).

In the real application there is so much info embedded in this basic structure that the three links would be accessed/navigated again and again so many times that many hundreds of lines of code (and/or OCL expressions) would be written "here, there and everywhere". One of the bad things with this is that if you explicitly traverse these links every time you need to access an object/attribute in the other end of the structure, you will need to execute this logic in the CPU every time.

Another bad thing is that your core business logic would "drown" in the code and the expressions dealing with this trivial navigation like "if then else, for i :=" and assign checks. This is where derived links can help a lot to avoid a logical mess (derived attributes would too, of course).

Fig 2: A simple class model of the illustration above could look something like this :

2.jpg 

The navigation problem is more obvious in this class model than in the "RIL world illustration".

If I selected a Parcel in a GUI list and needed info from the trip object I couldn't be sure of the exact navigation path to the "mobilized" trip object which is the one holding the valid trip info. Example:

If the parcel was loaded on a trailer the expression 'vehicleUnit.trip' would return a demobilized trip object, but if the parcel was loaded on the hauler it would return the desired "mobilized" trip object. This complication is reason enough to add a derived link fixing the problem directly in the model so that one never again would have to consider this when designing algorithms or navigating the structure. I would definitely add a link called "EffectiveTrip" as follows :

Fig 3: A link called "EffectiveTrip" was added to the model. (I always try to let blue color represent "persistent" (="freeze") and orange representing "derived" for links).

3.jpg

As I already can foresee that this link will be accessed for numerous vehicle combinations (in grids etc) I would hard code the derivation optimizing the logic here to find the "front most" vehicle and that means traversing the trailer/hauler link and stop when the hauler link is nil (=IsFrontMost). From there we can reference the trip object directly. But I also know that finding the front most vehicle (the combination hauler) is very typical and will happen even more often than accessing the trip object itself. I already know that the EffectiveTrip link as well can use such a derived link! Lets add it right away giving it the name 'CombinationFirst'.

I choosed the name 'CombinationFirst' because then you immediately realize the typical in this situation. It's a typical list handling problem. You can already imagine the usefulness of a another derived link which we can call 'CombinationLast' and even a third link called 'CombinationUnits'!

I can assure that these links will be frequently used and thus the "cost" of evaluating them will be paid off already the second time you use any one of them !

Fig 4: Doesn't also derived links require CPU in order to be evaluated...? you might ask. The answer is, yes of course - but only once - if the subscribed items don't change. And this is not very likely here, but the very "hot spot" structure here will be used in many places in the logic accessing objects cross over very often and when doing so the links already directly references the instances you want to access.  Look at the "mess" below... ! :

 4.jpg

This a high performance solution using the model as the tool for optimization, and at the same time the additional links are clarifying/indicating directly in the model the intended usage of this specific structure !

For those of you who think that I finally got mad I can tell that I didn't. Instead I increased the speed of the calculations and all the activities related to this structure in a real world application with many thousand percents compared to explicitly defining the entire navigation path each time the combination trip or a specific Vehicle unit (or attribute in any of them) was accessed !

All kinds of data are stored in this structure and more than 30 derived attributes for different calculation purposes accesses the structure back and forth all the time providing the client user with real time calculations of revenues/km and distances, distance shares of totals, share of shares of... etc, etc.

The thing is to let one derived link use the other links as often ass possible, then even the need for reevaluating the links when things changes decreases!

The last statement can be verified very clearly with a separate example showing how important it is to "REUSE" everything you derive to every higher level (regarding which part of the structure is more likely to change and which part is more "static"). With "reuse" I mean "the CPU work already done" in deriving the link. More on this could be subject for a separate article.

Now look at the code. We need to get hold of the trip object (for some reson), starting from a Parcel, but we don't know on which VehicleUnit we are loaded so we start looking for it using the link "EffectiveTrip" which "hides" the logic determining which trip object is "mobilized" (i.e "effective") :


function TParcel.CalculateTrip_Something...: Double;
var
  TripObj : TTrip;
begin
  TripObj := vehicleUnit.effectiveTrip; // that's it! ...

Easy, this is clear code. The details about how we got hold of the correct trip object should not be mixing up the business logic dealing with access or calculations in the object structure.

In our real world application both VehicleUnits and Parcels have a common super class (not shown here). A vehicle can be loaded on another vehicle, just like any other parcel so the code above is even simplier in my final generic model where the derived link is "virtual overrided" in different subclasses. My final code goes like this :


begin

  TripObj := effectiveTrip; // that's it!

The Parcel will find its valid trip using this command, regardless of how complicated the path might be. This is shown in fig 5 where a new common super class actually owns the persistent and derived link to the trip class and the EffectiveTrip-link is implemented first in the TParcel and then overrided for the VehicleUnit class as shown below.

Fig 5: Here I modified the model to become more generic allowing any "planable object" to carry a batch of other planable objects - and as the model goes more generic the role names do so too.

 
5.jpg 

Trip info applies directly for a Parcel too (via AbstractPlanPortion) because a transport company can plan a parcel to be delivered with, for instance, an aeroplane which totals we are not interested of - but we are still interested in keeping track of any trip events involved in the actual transportation, and documentation and transactions attached to the parcel itself etc. But this also means that when there's need for accessing the "effective" trip object we also do need to perform very complicated checks all over the structure trying to find out in which context the (correct, "mobilized") trip object can be found...  This will really make our logic somewhat complicated for such a simple thing as simply retrieving some data from the structure. A real nightmare in fact... 

This complication of a simple thing is not very unique. What is unique is how it can be dealt with using Bold technology with its derived links.

The Parcel can be the TripHolder itself, or if the Parcel is loaded on a carrier it don't know if it's loaded on the carrier which actually holds the trip info. We would really need to do some navigatiion here. In a situation like this you can easily imagine how much coding and how complicated OCL expressions would be accessing information in the structure. Your core business logic would "drown" in navigation logics when simply retrieving trivial things from the structure and the CPU would spend lots of time finding Your way(s) to the target objects/data.

Finally : Three (3) persistent links holding the structure all together ended up in another five (5) very useful derived links ! The result is that the final application runs much faster and business logic is held more apart from trivial navigation endlessly traversing object structures.

Before listing the most efficient code for the optimized hardcoded derivations of the links, I would like to conclude that with these links you can, from anywhere in the structure, go anywhere - directly - by referencing link members with names fully clarifying what you intend to access, and this will be done in the most efficient way you can come up with using the regular Bold architecture. 

With "efficient" I mean "always direct access to the desired target object instance" (except for the first evaluation wich in my case happens only once for hundreds of accesses...).

This is only part of the concepts we used consciously to make possible what was not possible before : using regular, but "clean design", modeled and structured object oriented business classes performing high performance real time (re)calculations of very advanced trip calculations (not discussed here). On a single CPU (application server) for multiple clients.

Derivation code, with comments

In the code below I underlined all the internal referencing of the derived links ("efficient reuse of already evaluated results").

I also used local variables thus avoiding the internal "look up" of Bold members more than once (=> avoiding repeated triggering of internal bold events etc).

The efficiency of the entire concept discussed and the detailed coding below is verified using ProDelphi profiler (very high accuracy (+-3%) on measuring code performance).


{ TParcel }



procedure TParcel._EffectiveTrip_DeriveAndSubscribe(...);
// If loaded the carriers (effective) trip is returned,
// else the local trip (if any).
var
  CarrierObj: TVehicleUnit;
  ResultValue : TTrip;
begin
  M_batchHolder.DefaultSubscribe(Subscriber, breResubscribe);
  CarrierObj := batchHolder;
  if Assigned(CarrierObj) then
  begin
    CarrierObj.M_EffectiveTrip.DefaultSubscribe(Subscriber, breResubscribe);
    ResultValue := CarrierObj.EffectiveTrip;
  end
  else
  begin
    M_trip.DefaultSubscribe(Subscriber, breResubscribe);
    ResultValue := trip;
  end;
  M_EffectiveTrip.BoldObject := ResultValue;
end;



{ TVehicleUnit }



procedure TVehicleUnit._EffectiveTrip_DeriveAndSubscribe(...);
var
  HaulerObj: TVehicleUnit;
  ResultValue,
  TripObj: TTrip;
begin
  ResultValue := nil;
  M_CombinationFirst.DefaultSubscribe(Subscriber, breResubscribe);
  HaulerObj := CombinationFirst;
  // the traversing is already done here
  if Assigned(HaulerObj) then
  begin
    HaulerObj.M_Trip.DefaultSubscribe(Subscriber, breResubscribe);
    TripObj := HaulerObj.trip;
    if Assigned(TripObj) then
    begin
      TripObj.M_IsMobilized.DefaultSubscribe(Subscriber);
      if TripObj.IsMobilized then
        ResultValue := TripObj;
    end;
  end;
  M_EffectiveTrip.BoldObject := ResultValue;
end;



procedure TVehicleUnit._CombinationFirst_DeriveAndSubscribe(...);
// This link will be the fast "short cut" used by many many
// functions in this scope and other links and attributes, thus
// meaning optimization, not "extras" or "candy" in the model.
var
  LoopObj: TVehicleUnit;
begin
  LoopObj := Self; // Traverse ahead
  LoopObj.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
  while Assigned(LoopObj.Hauler) do
  begin
    LoopObj := LoopObj.Hauler;
    LoopObj.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
  end;
  M_CombinationFirst.BoldObject := LoopObj;
end;




procedure TVehicleUnit._CombinationLast_DeriveAndSubscribe(DerivedObject: TObject; Subscriber: TBoldSubscriber);
var
  LoopObj: TVehicleUnit;
begin
  LoopObj := Self;
  LoopObj.M_trailer.DefaultSubscribe(Subscriber, breResubscribe);
  while Assigned(LoopObj.trailer) do
  begin
    LoopObj := LoopObj.trailer;
    LoopObj.M_trailer.DefaultSubscribe(Subscriber, breResubscribe);
  end;
  M_CombinationLast.BoldObject := LoopObj;
end;




procedure TVehicleUnit.CombinationUnits_DeriveAndSubscribe(...);
var
  LoopObj: TVehicleUnit;
  ResultList: TBoldObjectList;
begin
  M_CombinationUnits.Clear;
  ResultList := TBoldObjectList.Create;
  try
    M_CombinationFirst.DefaultSubscribe(Subscriber, breResubscribe);
    LoopObj := CombinationFirst;
    repeat
      ResultList.Add(LoopObj);
      LoopObj.M_trailer.DefaultSubscribe(Subscriber, breResubscribe);
      LoopObj := LoopObj.trailer;
    until LoopObj = nil
  finally
    M_CombinationUnits.AddList(ResultList);
    FreeAndNil(ResultList);
  end;
end;




procedure TVehicleUnit._CombinationLoadItems_DeriveAndSubscribe(DerivedObject: TObject; Subscriber: TBoldSubscriber);
// A list collecting all (batch)items loaded on any unit in the
// vehicle combination. Implemented for convenience and
// clearification.
var
  UnitCnt, i: Integer;
  UnitObj: TVehicleUnit;
begin
  M_CombinationLoadItems.Clear;
  CombinationUnits.DefaultSubscribe(Subscriber, breResubscribe);
  UnitCnt := CombinationUnits.Count;
  if UnitCnt > 0 then
  begin
    for i := 0 to UnitCnt-1 do
    begin
      UnitObj := CombinationUnits[i];
      UnitObj.loadedItems.EnsureObjects;
      UnitObj.loadedItems.DefaultSubscribe(Subscriber);
      // Collect all if UnitObj.loadedItems.Count > 0 then
      M_CombinationLoadItems.AddList(UnitObj.loadedItems);
    end;
  end;
end;

// Rolf Lampa

(Current mail address: rolf-dot-lampa-at-rilnet-dot-com)

 

Share this article!

Follow us!

Find more helpful articles: