Stefano Tommesani

  • Increase font size
  • Default font size
  • Decrease font size
Home Programming Further multi-thread processing with Delphi

Further multi-thread processing with Delphi

In a previous article named "Easy multi-thread programming Delphi", the AsyncCalls library was used to process multiple images at the same time. However, the processing of every single image was still strictly serial, even if image processing kernels are quite easy to accelerate spreading the load over multiple threads.

In this article we will see how the OmniThreadLibrary can be used to split a simple image processing kernel across multiple threads.

procedure TProcessedImage.EffectBlackWhite(Bitmap : TBitmap32);
var x, y : integer;
    Color : TColor32;
    Red, Green, Blue : Cardinal;
    Gray : Cardinal;
begin
  Bitmap.BeginUpdate;
  try
    for y := 0 to Pred(Bitmap.Height) do
        for x := 0 to Pred(Bitmap.Width) do
            begin
            Color := Bitmap.Pixel[x, y];
            Red := (Color and $00FF0000) shr 16;
            Green := (Color and $0000FF00) shr 8;
            Blue := Color and $000000FF;
            Gray := (Red * 299 + Green * 587 + Blue * 114) div 1000;
            //Io=(0.299Ri + 0.587Gi + 0.114Bi)
            Bitmap.Pixel[x, y] := Color32(Gray, Gray, Gray);
            end;
  except
  end;
  Bitmap.EndUpdate;
end;

This procedure converts a RGB image into a gray-scale image by replicating the luma value into all the RGB channels. The processing of each pixel is independent from other ones, so it can be computed in parallel. Still, allocating a thread for each pixel would generate a huge unnecessary overhead, so it is more efficient to process each line, containing Bitmap.Width pixels, in a separate thread. Moving to a parallel solution is very easy thanks to the OmniThreadLibrary:

  1. include OtlParallel into the uses list
  2. add a Parallel.ForEach().Execute() line that specifies the number of iterations of the loop, in this case the vertical rows of the image to be processed, so the loop counter goes from 0 to Pred(Bitmap.Height) as the loop handled by the y variable in the serial code
  3. move the bulk of the image processing code into a lambda inside the Execute invocation, and replace the variable y with the argument of the lambda called elem
  4. move the variables that work on a single pixel inside the lambda, outside of the parent procedure, or they will be shared across all the threads executing the lambda. If you encounter wrong results that vary at every run, check that all variables used to perform computations are declared in the lambda.
procedure TProcessedImage.EffectBlackWhite(Bitmap : TBitmap32);
begin
  Bitmap.BeginUpdate;
  try
    Parallel.ForEach(0, Pred(Bitmap.Height)).Execute(
    procedure (const elem: integer)
    var x : integer;
        Color : TColor32;
        Red, Green, Blue : Cardinal;
        Gray : Cardinal;
    begin
      for x := 0 to Pred(Bitmap.Width) do
          begin
          Color := Bitmap.Pixel[x, elem];
          Red := (Color and $00FF0000) shr 16;
          Green := (Color and $0000FF00) shr 8;
          Blue := Color and $000000FF;
          Gray := (Red * 299 + Green * 587 + Blue * 114) div 1000;
          //Io=(0.299Ri + 0.587Gi + 0.114Bi)
          Bitmap.Pixel[x, elem] := Color32(Gray, Gray, Gray);
          end;
    end);
  except
  end;
  Bitmap.EndUpdate;
end;

That's all! I told you this was going to be easy... Thanks to the OmniThreadLibrary, splitting a computational load over multiple CPU cores is now a task that can be accomplished in a matter of minutes.

Even better, we can use OmniThreadLibrary to build DUnit test cases that check for code correctness under multi-threaded usage. The following code fragment tests that adding status updates and queries can be run at the same time on the database backend:

procedure TestTFWDDatabase.TestMultiThread3;
const UpdatedUserName : string = 'UpdatedUser';
      UpdaterUserName : string = 'UpdaterUser';
var UpdatedUser : TFWDUser;
    UpdaterUser : TFWDUser;
    CleanupStatusChangeList : TList<TFWDStatusChange>;
    StatusChangePtr : TFWDStatusChange;
begin
  /// create user to be updated
  UpdatedUser := TFWDUser.Create;
  UpdatedUser.ProtocolId := UpdatedUserName;
  ObjectDatabase.AddUser(UpdatedUser);

  /// create user that will signal updates
  UpdaterUser := TFWDUser.Create;
  UpdaterUser.ProtocolId := UpdaterUserName;
  ObjectDatabase.AddUser(UpdaterUser);

  /// add updates and run queries on update table at the same time
  Parallel.ForEach(1, 100).Execute(
    procedure (const elem: integer)
    var TestStatusUpdate : TFWDStatusUpdate;
        StatusChangeList : TList<TFWDStatusChange>;
    begin
    if (elem and 1) = 0
       then begin
            /// status update
            TestStatusUpdate := TFWDStatusUpdate.Create(UpdaterUserName, PROTOCOL_FACEBOOK);
            TestStatusUpdate.AddFriendStatusUpdate(UpdatedUserName, false, elem);
            TestStatusUpdate.Process;
            TestStatusUpdate.Free;
            end
       else begin
            /// run query
            StatusChangeList := ObjectDatabase.GetStatusChangesFromUserId(UpdatedUser.Id);
            StatusChangeList.Free;
            end;
    end
  );

  CleanupStatusChangeList := ObjectDatabase.GetStatusChangesFromUserId(UpdatedUser.Id);
  Check(CleanupStatusChangeList.Count = 50, 'Wrong count of status changes');
  Check(UpdaterUser.UpdateCount = 50, 'Wrong count of status updates');
  /// additional tests hidden...

  /// cleanup
  for StatusChangePtr in CleanupStatusChangeList do
      ObjectDatabase.EraseStatusChange(StatusChangePtr);
  CleanupStatusChangeList.Free;

  ObjectDatabase.EraseUser(UpdatedUser);
  ObjectDatabase.EraseUser(UpdaterUser);
end;

The Parallel.ForEach runs 100 iterations of the lambda function, that uses the elem parameter to choose which action shall be performed in that invocation, choosing between adding a status update on even iterations and running a query on status tables on odd iterations. This code fragment is a part of the DUnit test suite of the Friend Watchdog server, and together with a large set of the other automated unit tests ensures that the server can support multiple requests from Friend Watchdog clients without getting stuck or corrupting data.

Quote this article on your site

To create link towards this article on your website,
copy and paste the text below in your page.




Preview :


Powered by QuoteThis © 2008
Last Updated on Monday, 22 April 2013 15:45  
View Stefano Tommesani's profile on LinkedIn

Latest Articles

Castle on the hill of crappy audio quality 19 March 2017, 01.53 Audio
Castle on the hill of crappy audio quality
As the yearly dynamic range day is close (March 31st), let's have a look at one of the biggest audio massacres of the year, Ed Sheeran's "Castle on the hill". First time I heard the song, I thought my headphones just got
Necessary evil: testing private methods 29 January 2017, 21.41 Testing
Necessary evil: testing private methods
Some might say that testing private methods should be avoided because it means not testing the contract, that is the interface implemented by the class, but the internal implementation of the class itself. Still, not all
I am right and you are wrong 28 December 2016, 14.23 Web
I am right and you are wrong
Have you ever convinced anyone that disagreed with you about a deeply held belief? Better yet, have you changed your mind lately on an important topic after discussing with someone else that did not share your point of
How Commercial Insight changes R&D 06 November 2016, 01.21 Web
How Commercial Insight changes R&D
The CEB's Commercial Insight is based on three pillars: Be credible/relevant – Demonstrate an understanding of the customer’s world, substantiating claims with real-world evidence. Be frame-breaking – Disrupt the
Windows Forms smells funny, but... 07 April 2016, 15.38 Software
Windows Forms smells funny, but...
In the "2016 .NET Community Report" just released by Telerik, the answers to the question "What technology would you choose if building for Windows Desktop?" were as follows: So roughly half of new desktop developments would

Translate