Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.
If you find these projects useful please consider becoming a sponsor with Patreon, GitHub or Liberapay.

Tuning external message pump performance

petko
Posts: 52
Joined: Sun Jul 02, 2017 9:58 am

Tuning external message pump performance

Post by petko »

I use CEF in my application with MultiThreadedMessageLoop = false and ExternalMessagePump = true. When the app is under stress (for example when starting up and when starting more than one Chromium component at the same time) it does not load all images in the browser and later I get "Failed to load resource: net::ERR_NAME_NOT_RESOLVED" errors for these images in the Chromium Dev Tools.

So, I suspect that CEF can't load the resources on time and just drops them (OnBeforeResourceLoad is never called for these images, but a custom scheme handler is). However I need to load the images from a custom resource handler and not from the custom scheme handler).

Running with MultiThreadedMessageLoop = true resolves this issue, but I can't use it in my app due to other technical reasons.

So, what are the implications of changing the CEF work scheduler settings DepleteWorkCycles and DepleteWorkDelay on performance? What about calling ProcessMessages more often in my app?

P.S.: Of course, if you have any idea why OnBeforeResourceLoad is not always called but the custom scheme handler is, please let me know.
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Tuning external message pump performance

Post by salvadordf »

Hi,
petko wrote: Tue Jan 23, 2018 11:59 am I use CEF in my application with MultiThreadedMessageLoop = false and ExternalMessagePump = true. When the app is under stress (for example when starting up and when starting more than one Chromium component at the same time) it does not load all images in the browser and later I get "Failed to load resource: net::ERR_NAME_NOT_RESOLVED" errors for these images in the Chromium Dev Tools.
So, I suspect that CEF can't load the resources on time and just drops them (OnBeforeResourceLoad is never called for these images, but a custom scheme handler is). However I need to load the images from a custom resource handler and not from the custom scheme handler).
Running with MultiThreadedMessageLoop = true resolves this issue, but I can't use it in my app due to other technical reasons.
CEF4Delphi uses the GlobalCEFApp.OnScheduleMessagePumpWork event to schedule the next call to GlobalCEFApp.DoMessageLoopWork.
GlobalCEFWorkScheduler handles all those events the best it can but if the CPU is too busy, some calls to GlobalCEFApp.DoMessageLoopWork may be too late, creating timeouts in the browser.

GlobalCEFWorkScheduler is almost a direct translation from the original CEF3 code to Delphi. It uses timers, which means that it generates WM_TIMER messages. If I remember correctly, WM_TIMER messages have lower priority which could make matters worse if the CPU is busy.

Perhaps wrapping GlobalCEFWorkScheduler in a high priority thread with synchronized calls to GlobalCEFApp.DoMessageLoopWork could be the solution to this problem.
petko wrote: Tue Jan 23, 2018 11:59 am So, what are the implications of changing the CEF work scheduler settings DepleteWorkCycles and DepleteWorkDelay on performance?
Those settings are only used in the GlobalCEFWorkScheduler destruction. They are not related to the app performance.
petko wrote: Tue Jan 23, 2018 11:59 am What about calling ProcessMessages more often in my app?
Please, don't call Application.ProcessMessages.

GlobalCEFWorkScheduler is EXTREMELY sensible to those calls and your app may freeze or have unexpected problems.
petko
Posts: 52
Joined: Sun Jul 02, 2017 9:58 am

Re: Tuning external message pump performance

Post by petko »

salvadordf wrote: Tue Jan 23, 2018 2:07 pmPerhaps wrapping GlobalCEFWorkScheduler in a high priority thread with synchronized calls to GlobalCEFApp.DoMessageLoopWork could be the solution to this problem.
I am not sure what you mean exactly: should I create a high-priority thread in my app, where to create the scheduler or should I modify the DCef source for the TCefWorkScheduler?
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Tuning external message pump performance

Post by salvadordf »

GlobalCEFWorkScheduler can be replaced if you need something different.
You can create a customized scheduler for your app and call it from the GlobalCEFApp.OnScheduleMessagePumpWork event.

However, it's clear that the current GlobalCEFWorkScheduler can be improved and I'm modifying it right now to use a thread without timers.

The next CEF4Delphi version will have a new GlobalCEFWorkScheduler.
petko
Posts: 52
Joined: Sun Jul 02, 2017 9:58 am

Post by petko »

Great! I will wait for it then, and will give you feedback if it improves things at least for my app.
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Tuning external message pump performance

Post by salvadordf »

Please download the latest version of CEF4Delphi.

It has the new GlobalCEFWorkScheduler that should improve performance.

GlobalCEFWorkScheduler now uses a thread to handle the work with a TEvent.

Use these GlobalCEFWorkScheduler properties :
  • GlobalCEFWorkScheduler.Priority : Changes the thread priority.
  • GlobalCEFWorkScheduler.DefaultInterval : Sets a new default time interval in milliseconds. GlobalCEFWorkScheduler schedules the GlobalCEFApp.DoMessageLoopWork calls when the GlobalCEFApp.OnScheduleMessagePumpWork event is triggered but it also calls GlobalCEFApp.DoMessageLoopWork at regular intervals defined by this property.
If you set the priority too high or you use a very small interval, your CPU could reach 100% usage.
Let me know if it works fine with the default property values.
petko
Posts: 52
Joined: Sun Jul 02, 2017 9:58 am

Post by petko »

With the new scheduler both BeforeResourceLoad and GetResourceHandler events are never called for my browsers and since I use them to serve my content, I can't test the updates at this time..
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Tuning external message pump performance

Post by salvadordf »

As far as I know the WorkScheduler should not have any effect on the events.

I tested the MDIExternalMessgePump and added those events to the child windows like this :

Code: Select all

procedure TChildForm.Chromium1BeforeResourceLoad(Sender: TObject;
  const browser: ICefBrowser; const frame: ICefFrame;
  const request: ICefRequest; const callback: ICefRequestCallback;
  out Result: TCefReturnValue);
begin
  Result := RV_CONTINUE;
end;

procedure TChildForm.Chromium1GetResourceHandler(Sender: TObject;
  const browser: ICefBrowser; const frame: ICefFrame;
  const request: ICefRequest; out Result: ICefResourceHandler);
begin
  Result := nil;
end;
I set 2 breakpoints in the 'Result' lines and opened 4 child browsers. The child browsers loaded the google home page which is very light but the breakpoints worked.

Please, test the websites that you load in your app with the MDIExternalMessgePump demo and check that the previous breakpoints work for you.
petko
Posts: 52
Joined: Sun Jul 02, 2017 9:58 am

Re: Tuning external message pump performance

Post by petko »

The problem was that I was setting Chromium events after calling CreateBrowser (I use ChromiumWindow and not CEFWindowParent) and for some reason none of these events was fired. I now set the events before creating the browser and the events work.

I have mixed success with the original issue though. The browser feels more responsive than before, but sometimes images are not loaded nevertheless.. I will experiment further with priority and intervals though..
User avatar
salvadordf
Posts: 4564
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Tuning external message pump performance

Post by salvadordf »

If you can't find a way to load all of your web pages correctly at the same time you can also delay some of them.
For example, load 2 at a time and then the next 2 pages.

Also check that the code in the BeforeResourceLoad and GetResourceHandler events can work correctly under heavy load.
Post Reply