-
Notifications
You must be signed in to change notification settings - Fork 0
6. Basic Examples
Content:
- 6. Basic Examples
- 6.1 Overview
- 6.2 Multithreading with GIL enabled
- 6.3 Multithreading with GIL disabled
- 6.4 Multiprocessing
The examples presented in this wiki page represent a development transition from fundamental to everyday programs which are designed with an added emphasis on:
- developing GUI applications with XCOFDK for Python,
- considerations related to application's task model,
- program performance, especially its responsiveness,
- the impact of GIL and achievable performance improvement if disabled,
- developing of program's UI content rapidly constructed using framework's subsystem for messaging.
The choice of providing the basic examples as GUI programs is chiefly motivated by the fact that the graphical presentation is best suited to illustrate the intended purpose. On the other hand, it also demonstrates the unique pattern of use of framewokr's public API by a program regardless of whether it is a console or GUI application.
There are two implementation versions available for the multithreading examples presented here:
- also refered to as RC examples, this group makes use of RC tasks, i.e. instances of
RC task classes designed for rapid construction.
They are located in subfolder xcofdk/tests/xuserapp/basic/xmt/rc/ and preferably presented in this wiki page, - also refered to as SC examples, this group makes use of subclassing approach with user-defined task classes
derived from abstract SC task classes.
They are located in subfolder xcofdk/tests/xuserapp/basic/xmt/sc/ and provided for demonstration purposes only.
NOTE:
- Even though the public API of the framewrok is a rich set of both interfaces classes and functions, each
of the examples only use a small subset of them according to their specific functionality.- And as all examples use framework's support for multithreading and/or multiprocessing out-of-the-box,
the programming effort needed to develop each of them is essentially given by the application-specific
complexity, especially by their specific UI layout of GUI applications.- The example program runs shown in this wiki page all use the stable Python version 3.14.0 which the
framework of XCOFDK considers the first Python release officially supporting free-threading (FT):
- For convenient, especially for experimenting purposes, all examples are also available as archive files:
Almost all programs presented here are multithreaded GUI applications designed according to the well-known pattern Model-View-Controller¹ (MVC). The class diagram below illustrates the common design of the GUI's base view:
-
UserAppControllerIF:
common base interface with application's controller component (with access to both model and view) is derived from, -
UserAppModelIF:
common base interface with application-specific data model (or container) is derived from, -
UserAppViewIF:
common base interface with application-specific view(s) are derived from, -
STGuiAppWelcome:
base, concrete view class derived from the above-mentioned interface class UserAppViewIF.
As indicated in the previous section, the class
STGuiAppWelcome
is designed to serve as a reusable basis for the graphical interface of all GUI applications.
A given basic example creates an instance of STGuiAppWelcome (or just the GUI for short) while adding
application-specific child views to it to achieve its specific graphical interface. The general grid layout is
depicted below:
# Simplified grid layout of class 'STGuiAppWelcome':
# +=============================================+
# | Info frame |
# +---------------------------------------------+
# | GUI action frame |
# +---------------------------------------------+
# | Child frames (if any)... |
# +=============================================+The implementation of the graphical interface provided by class
STGuiAppWelcome
usesn the GUI toolkit tkinter².
The main responsibility of the GUI is to create the root window (including child views) and to manage its event loop
when started. Two optional command line arguments can be supplied:
-
--help
print usage and exit, -
--disable-auto-start
do not run the GUI Action by default upon program start. It defaults to False, -
--enable-auto-close
close the GUI immediately if the GUI Action is not running currently, or after its completion otherwise. It defaults to False.
The code snippet below shows how to start the GUI via command line as a standalone, singlethreaded program:
$> cd /path/to/parent/folder/of/stguiappwelcome.py/
$>
$> # run the script for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m stguiappwelcomeThe screenshot below shows the output of the program:
The GUI Action frame shown above is designed as a replacement of whatever an arbitrary application would have to perform in terms of GUI operations. It especially also represents program's most important performance metric, namely the responsiveness, by providing a graphical indicatior consisting of a progress bar and the amount of time it takes for the GUI to refill the progress bar when restarted.
Unless stopped or paused by the user, the GUI Action is restarted by the GUI in a cyclic manner, except for the singlethreaded execution of STGuiAppWelcome shown above. While the perceived speed of the progress bar (when running) is a graphical visualization of the quality of current responsiveness already, a rough by-value-comparison of the elapsed time compared to the singlethreaded version may provide more expressive assessments (of the task model) of the example at hand.
Common for all example programs is a main module which provides a Main() function acting as the entry point of the application. Its general layout is inspired by the common pattern of use of the control functions of the subsystem fwctrl. When called, the main responsibility of Main() is as follows:
- modify the RTE configuration (if needed),
- start of the framework,
- create and start applications main task (acting as the starter task),
- wait for the framework to terminate.
As discussed in section 4.2.2 3-PhXF of Task Instances, the execution frmae of an RC task can be specified either by ordinary callback functions or by (an instance) of a XF-like class. The RC examples, however, are rather developed using the XF-like approach as most of the applications tasks do also have to maintain their specific per-instance data which directly or indirectly contribute to the UI content to be presented.
The class diagram below illustrates the application design common for all RC example:
-
XFMainTask:
represents the execution frame of application's main task which is a user-defined XF-like class:-
derived from UserAppControllerIF, it is the controller of application's MVC design, too,
-
instance member gui refers to the GUI instance of the application,
-
the main task may or may not maintain service tasks (see below), child processes and the model instance of application's MVC design,
-
instance member myTask refers to the current running task which executes the main task.
With the code snippet below given (see RC exampleB11), more precisely it refers to the RC task instance, e.g. _myMT below, created by supplying an instance of this class, e.g. _phasedXFCallback below, as its execution frame:# file : rc/exampleB11/mtguiapp.py #... from xuserapp.basic.xmt.rc.exampleB11.maintask import _CreateMainTask def Main(cmdLineOpts_ : CLOptions): #... # step 2: create main task _myMT = _CreateMainTask(cmdLineOpts_) # step 3: start framework fwapi.StartXcoFW(fwStartOptions_=cmdLineOpts_.GetSuppliedFwOptions() # step 4: start main task _myMT.Start('sample positional argument', kwArg_='sample keyword argument') #...
# file : rc/exampleB11/maintask.py #... from xcofdk.fwapi import XFSyncTask from xcofdk.fwapi import XFAsyncTask from xuserapp.st.welcome.interfaces.controllerif import UserAppControllerIF def _CreateMainTask(cmdLineOpts_ : CLOptions) -> IRCTask: _aliasName = 'MainTask' _phasedXFCallback = XFMainTask(cmdLineOpts_) if cmdLineOpts_.isAsynMainTaskEnabled: res = XFAsyncTask(_phasedXFCallback, aliasName_=_aliasName, bMainTask_=True) else: res = XFSyncTask(_phasedXFCallback, aliasName_=_aliasName, bMainTask_=True) return res class XFMainTask(UserAppControllerIF): #...
The main task especially serves as application's starter task, that is that task instance (of application's task model) which is started as the first one once the framework is started, as shown by step 4 above,
-
-
XFServiceTask:
represents the execution frame of a service task which is a user-defined XF-like class, too:- in general, service tasks (if any) are designed to perform some application-specific communication with the main task via subsystem messaging,
- instance member myTask refers to the current running task which
executes the respective instance of this class.
With the code snippet below given (see servicetask.py), more precisely it refers to the RC task instance, e.g. the returned value res below, created by supplying an instance of this class, e.g. _phasedXFCallback below, as its execution frame:# file : rc/rcmisc/servicetask.py #... from xcofdk.fwapi import XFAsyncCommTask def CreateServiceTask( srvTaskNo_ : int , bBroadcast_ =False , bSkipPayloadSerDes_ =False , bCustomPayLoad_ =False) -> IRCCommTask: #... _phasedXFCallback = XFServiceTask( bBroadcast_=bBroadcast_ , bSkipPayloadSerDes_=bSkipPayloadSerDes_ , bCustomPayLoad_=bCustomPayLoad_) res = XFAsyncCommTask(_phasedXFCallback, aliasName_=_aliasName, runCallbackFrequency_=20) return res class XFServiceTask: #...
The SC examples are the subclassing counterpart of the RC examples, so they provide exactly the same program behavior. They differ from the RC examples in that both the main task and service tasks are created following the subclassing approach. The class diagram below illustrates the application design common for all SC example:
-
AppMainTask:
represents application's main task, e.g. exampleSCB11.MainTask, derived from the abstract class XMainTask.
As for RC examples, the main task is derived from UserAppControllerIF by multiple inheritance, so it acts as the controller of application's MVC design, too, -
AppServiceTask:
represents a service task, e.g. sc.scmisc.ServiceTask, derived from the abstract class XTask.
An overview of optional command line arguments available for all basic GUI examples is shown in the table below:
| Argument | : | Description |
|---|---|---|
| -h | : | print usage and exit |
| --help | : | ditto |
| --log-level LOG_LEVEL | : | see API function StartXcoFW() |
| --fw-log-level FW_LOG_LEVEL | : | ditto |
| --disable-log-timestamp | : | ditto |
| --disable-log-highlighting | : | ditto |
| --disable-log-callstack | : | ditto |
| --bypass-free-threading-guard | : | see API function RtePolicyBypassExperimentalFreeThreadingGuard() |
| --disable-auto-start | : | explained here |
| --disable-auto-close | : | ditto |
NOTE:
- Color highlighting of the log output is supported for Python versions 3.9 and higher. In general, the start
option --disable-log-highlighting should always be supplied whenever used console program of
the platform, e.g. Windows Commond Prompt cmd.exe, does not support colored output to stdout.
- The framework supports the stable version 3.14.0 being the first Python version officially supporting
free-threaded (FT) Python.- Python versions 3.13 and pre-releases of 3.14.0 are rather considered by the framework supporting
experimental free-threaded Python only.
Accordingly, whenever using these interpreter versions, a pre-configuration of the RTE via the policy
function RtePolicyBypassExperimentalFreeThreadingGuard() remains mandatory.
The GUI application exampleB11 is the multithreaded counterpart of the singlethreaded base view STGuiAppWelcome. It uses its main task to (a)synchronously start the GUI.
Application modules:
-
mtguiapp.py:
following the common pattern of use, the main module uses the related API for pre-configuration of the RTE to disable below subsystems as this example makes no use of them: -
maintask.py:
- in accordance to the common application design of RC examples, it provides the class XFMainTask which represents main task's execution frame derived from the MVC interface class UserAppControllerIF,
- as for all GUI examples presented in this wiki page, the non-cyclic main
task is basically responsible for:
- starting the GUI instance of the program,
- performing the same functional operation of the GUI action as done by its singlethreaded counterpart,
- providing a few additional runtime information related to the main task,
- and update of GUI's UI content based on application's MVC design accordingly.
- service task: N/A
Optional command line argument(s):
In addition to the common program options, available optional command line argument(s) are as follows:
| Argument | : | Description |
|---|---|---|
| --async-main-task | : | make the execution type of the main task is asynchronous instead of synchronous |
The code snippet below shows how to start exampleB11 via command line:
$> cd /path/to/subfolder/exampleB11/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mtguiappThe screenshot below shows the UI of the program (see also section 6.3.1 exampleB11 without GIL for a performance comparison):
The GUI application exampleB21 represents the generic master of a family of GUI applications designed to demonstrate basic features of the subsystem messaging. Except for the UI content constructed according to their specific use case of messaging, they all have a similar GUI appearance:
-
exampleB21:
for general introduction of the messaging subsystem using direct addressing policy, -
exampleB22:
for use of broadcast messages via alias addressing policy, -
exampleB23:
for use of message drive tasks, -
exampleB24:
for bypassing payload (de-)serialization (or marshaling) of individual messages, -
exampleB25:
for use of custom payload classes.
The following subsections discuss the example21 as the master representative of this group of example programs in more detail.
Application modules:
-
mtguiapp.py:
- the main module which uses the related API for pre-configuration of the RTE to disable the subsystem for multiprocessing which is not used,
- it also creates the main task of the application associated to the example at hand (see below),
-
maintask.py:
- in accordance to the common application design of RC examples, it provides the class XFMainTask which represents main task's execution frame derived from the MVC interface class UserAppControllerIF and capable of full communication,
- it is made quite parameterized to serve as a generic, configurable and resuable basis for all examples of this family,
- the non-cyclic main task is basically responsible for:
- starting the GUI instance of the program,
- create and start of the service tasks (see below),
- initiating continuous data exchange via messaging with the service tasks,
- update of GUI's UI content based on application's MVC design,
-
servicetask.py
(or servicetaskBXQ.py):
- it provides the class XFServiceTask (or XFServiceTaskBXQ, respectively) used as the execution frame of asynchronous, cyclic service task instances capable of full communication,
- in general service instances are also responsible for continuous messaging with the main task as long as they are running.
Optional command line argument(s):
In addition to the common program options, available optional command line argument(s) are as follows:
| Argument | : | Description |
|---|---|---|
| --async-main-task | : | make the execution type of the main task is asynchronous instead of synchronous |
| --service-tasks-count | : | number of service tasks (in the range of [1..12]) to be created and started by the main task, defaulting to 12 |
The code snippet below shows how to start exampleB21 via command line:
$> cd /path/to/subfolder/exampleB21/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mtguiappThe screenshot below shows the UI of the program with:
-
a GUI action performance of 2.740 [s], which is:
- ca. 110 [ms] slower than its execution without GIL,
- only ca. 210 [ms] slower than the singlethreaded execution of the GUI,
even though there are at least 12 additional (application) tasks runnig while GIL is around,
-
ca. 172 [msg/s] transferred, i.e. (sent or received) messages,
-
exampleB21 with GIL enabled:
As indicated above, the main functional purpose of this family of basic examples is to make a heavy use of the API of the messaging subsystem. For this, the main task periodically sends messages to its service tasks and processes their responses or messages. Therefore, the configuration of an appropriate messaging and/or run-phase frequency of each of the involved application tasks is the utmost parameter of the designed task model to make program's responsiveness can be ensured.
Also as for the simplicity, all service tasks of a given application of this family are always instances of the same class. This way, the design of the task model of these examples is basically given by the specification of two configuration items:
- the frequency of main task's messaging,
- the run-phase frequency of the service tasks.
Regardless of the chosen execution type, the run-phase frequency of the main task doesn't matter at all, as it is configured to be non-cyclic. But:
- as shown in section 6.1.3.1 Class Diagram of RC Examples, the execution frame of the main task, i.e. XFMainTask, is derived from the MVC interface class UserAppControllerIF, which is especially important, as it declares the main task to be the controller instance of application's MVC design,
- and doing so, the periodic messaging can take place with the aid of the event loop of its GUI instance, more
precisely via the callback function
XFMainTask.OnViewNotification.
That notification callback is called by the GUI in a frequency specified through the respective configuration item
below:
- EGuiConfig.eControllerNotificationFrequencyMS (set to 100 [ms] for all of the examples).
As opposed to the main task, serivce tasks do not have much to do, as they each merely process a few received messages (if any) and send out one single message (if any) per run-phase iteration, thereby not taking considerable CPU time.
Therefore, a run-phase frequency of 20 [ms] is a promising value to make service tasks are sending out as often as possible, while the total number of messages sent out remains manageable for the main task. So, for the use case of 12 service tasks (as shown in the screenshot above), the total number of messages sent by the service tasks (and received or processed by the main task) is ca. 150 [msg/s]. In other words, the main task has to process a new received message every 6.7 [ms].
The GUI application exampleB31 is a specialized version of above-presented exampleB21 with added focus on the run-phase frequency of the service tasks which have to execute an additional computation per run-phase loop iteration:
- in general, the example represents a sample program which might be used as an analysis and/or configuration tool with respect to the run-phase frequency of tasks of similar (GUI) applications, too,
- while the chosen approach remains unchanged, the outcome of the analysis directly depends on the number of (application) tasks of the program and available resources of the host machine, e.g. memory or number of CPU cores,
- the following discussion is particularly based on the fix number of 12 service tasks,
- the utility function UserAppUtil.Fibonacci() is used for the additional calculation requires a significant amount of time based on the non-negative integer number N passed to as input. It is a trivial implementation of the well-known Fibonacci sequence with no optimization applied to. However, note that the chosen utility function here is simply a placeholder for any possible CPU-bound computation to be performed by individual application tasks.
Application modules:
-
mtguiapp.py:
- the main module which uses the related API for pre-configuration of the RTE to disable the subsystem for multiprocessing which is not used,
- it also creates the main task of the application (see below),
-
maintask.py:
- in accordance to the common application design of RC examples, it provides the class XFMainTaskGIL which represents main task's execution frame derived from the MVC interface class UserAppControllerIF and capable of full communication,
- the non-cyclic main task is basically responsible for:
- dynamic, rough estimation of required run-phase frequency of the
service tasks for the Fibonacci input N supplied (see below). The table below shows a sample estimation
result based on the reference test environment used for the program runs on Ubuntu:
Fibonacci input N : CPU time cons. [ms] Run-phase freq. [ms] Deficient run-phase freq. [ms] 18 : 0.369 4 3 19 : 0.582 6 5 20 : 0.953 10 8 21 : 1.791 19 16 22 : 2.500 27 22 23 : 4.272 46 37 24 : 6.616 72 58 25 : 11.204 123 99 26 : 17.326 190 152 - starting the GUI instance of the program,
- create and start of the service tasks (see below),
- initiating continuous data exchange via messaging with the service tasks,
- update of GUI's UI content based on application's MVC design,
- dynamic, rough estimation of required run-phase frequency of the
service tasks for the Fibonacci input N supplied (see below). The table below shows a sample estimation
result based on the reference test environment used for the program runs on Ubuntu:
-
servicetaskGIL.py:
- it provides the class XFServiceTaskGIL, used as the execution frame of asynchronous, cyclic service task instances capable of full communication,
- in general service instances are also responsible for continuous messaging with the main task as long as they are running,
- they execute the calculation of the Fibonacci sequence for the supplied input N in each of their run-phase interation, too.
Optional command line argument(s):
In addition to the common program options, available optional command line argument(s) are as follows:
| Argument | : | Description |
|---|---|---|
| --fibonacci-input | : | integer value as input for calculation of Fibonacci sequence by a service task, valid range is: [18..26] (defaulting to 18) for Linux platform [17..26] (defaulting to 17) otherwise |
| --force-deficient-frequency | : | make service tasks are configured with a deficient run-phase frequency |
Note however, that the estimated (deficient) run-phase frequencies for a given Fibonacci input N may show an almost equal timing behavior of the program depending on the shortes achievable sleep time on the host machine.
The code snippet below shows how to start exampleB31 via command line with 20 supplied as Fibonacci input:
$> cd /path/to/subfolder/exampleB31/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mtguiapp --fibonacci-input 20The screenshot below shows the UI of the program:
Impact of GIL:
In general, a dedicated consideration of the run-phase frequency is a common issue of task model design of multithreaded applications (with or without CPU-bound activity) regardless of additional performance constraints like GIL for Python programs. Only, for Python interpreter versions built with no support for --disable-gil (or whenever sys._is_gil_enabled() (if available) returns True at runtime) the issue becomes an inevitable part of task model's design effort.
A mal-configured run-phase frequency would otherwise make that program's performance suffers from unblanced concurrency resulting in at least insufficient responsiveness of the application. As shown in the UI screenshot above, the average amount of time needed for the GUI action is around 4.0 [sec] for N=20, which is still a quite acceptable value on the reference test environment use for. A noticeable contrast shows up, as soon as the run-phase frequency is forced to be deficient for the same input N=20. Now, the GUI action takes around 5.1 [sec] (and more) with a perceivable jitter effect of the progress bar:
$> cd /path/to/subfolder/exampleB31/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mtguiapp --fibonacci-input 20 --force-deficient-frequencywith below screenshot of the corresponding UI:
A highly simplified explanation of the program's performance with mal-configured run-phase frequency is given when the application tasks only are considered, i.e. the main task and the service tasks alone. As GIL is present for the Python version running:
- the normal scheduling policy applied to tasks (or host threads) is suspended if currently executing a CPU-bound piece of code. Such a task will keep the CPU until completion of that piece of code,
- accordingly, the service tasks take a continuous, total time of 11.4 [ms] (ca. =12*0.953) to complete the current iteration of the run-phase before the GUI, i.e. the main task, is re-scheduled with CPU,
- but then, the GUI (as being basically IO-bound due to its event loop) doesn't get the chance of a fairly shared CPU, as the deficient frequency of 8 [ms] is already expired at least for the first service task waiting for CPU. Therefore, the GUI is more or less promptly scheduled off-CPU due to the re-scheduling done by operating system's scheduler,
- now, the service tasks regain the CPU for the next continuous 11.4 [ms], until the GUI is re-assigned with CPU for the next time,
- all in all, this high-frequency of re-scheduling with the GUI being interrupted over and over takes place all the time during program execution.
NOTE:
- The approach of determining the proper run-phase frequency (of individual tasks) presented here is
suitable to only a limited extent whenever GIL is around with CPU-bound tasks inherently requiring
a singnificant amount of CPU time, and so blocking other tasks,- for N=26, for example, the total amount of time needed by the service tasks to complete a given
iteration of the run-phase loop is 208.0 [ms] (ca. =12*17.326),- accordingly, the responsiveness of the GUI will be suffering anyway, even for sufficiently configured
run-phase frequency, as the GUI is left with a frequency of ca. 5 times a second only, which is definitely
too low.
The GUI application exampleB32 uses a synchronous main task and multiple asynchronous service tasks. It demostrates achievable improvement via optimization of CPU-bound tasks even despite GIL.
The section 6.2.3 exampleB31 with GIL discussed the impact of GIL, especially in the context of CPU-bound tasks for which there may or may not be some way of optimization available in terms of required CPU consumption. Apart from presenting one more hands-on example (for working with the public API of the framework), exampleB32 also is designed to point out below noticeable facts:
- the framework itself is designed and implemented with GIL in mind, its runtime behavior is not impacted by,
- multithreading and so the use of multiple application tasks may also help in delegation of program complexity,
- compared to their singlethreaded counterparts, multithreaded applications, if designed and configured properly, are capable of achieving almost the same or even much better (speed) performance for time critical and/or CPU-bound computation, especially whenever appropriate optimization can be provided.
Application modules:
-
mtguiapp.py:
- the main module which uses the related API for pre-configuration of the RTE to disable the subsystem for multiprocessing which is not used,
- it also creates the main task (see below),
-
maintask.py:
- in accordance to the common application design of RC examples, it provides the class XFMainTaskGIL2 which represents main task's execution frame derived from the MVC interface class UserAppControllerIF and capable of full communication,
- on a regular basis, the main task requests all service tasks to deliver Fibonacci(N) for a list of some selected N<=100,
- as soon as the first reply is received, the result is displayed,
- once all service tasks have replied their respective results which happens almost promptly, the request is considered serviced,
- after a short time, next request is sent out to the service tasks,
-
servicetaskGIL2.py:
- it provides the class XFServiceTaskGIL2, used as the execution frame of asynchronous, cyclic service task instances capable of full communication,
- by default, the service tasks (re-)start the calculation of Fibonacci(N) up to N=100 all the time,
- upon a request from the main task, they delete their respective cache and restart the calculation for the requested list of input N,
- as soon as done, they reply to the main task with a list of the calculated results,
- then, the default calculation of Fibonacci takes place again until next request from the main task is received,
- the optimization used for the Fibonacci calculation by the service instances is given by use of the caching mechanism on one hand (see ServiceTaskGIL2.__CalcFibo()), and allowing the calculation of Fibonacci in any of the iteration of the run-phase loop of the service instances for the next 20 integer numbers only on the other hand (see ServiceTaskGIL2.RunTask()).
Optional command line argument(s):
The available optional command line argument(s) of example32 are described in section 6.1.3 Common Program Options.
The code snippet below shows how to start example32 via command line:
$> cd /path/to/subfolder/exampleB32/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mtguiapp The screenshot below shows the UI of the program:
-
the whole calculation (for quite large numbers as the Fibonacci input N requested by the main task) takes less than 110.0 [ms],
-
accordingly, the performance of the GUI action is comparable to the one of the singlethreaded execution of the GUI,
even though there are at least 12 additional (application) tasks runnig while GIL is around, -
exampleB32 with GIL enabled:
Certainly, the trivial implementation of the utility function UserAppUtil.Fibonacci() would benefit from the optimization presented above, too, so the Fibonacci calculation could have been done by the main task instead. This approach, however, would at least significantly increase the programming complexity of the main task whose functional responsibility is actually the GUI, plus other high-level or coordinating stuff. Moreover, in case of the availability of an even better optimization, the main task and so its internal organization would have to be changed as well, whereas only the related service task class needs to be adapted only, if the delegation of functional responsibilty applied instead.
The code snippet and screenshot below show the UI of the same program presented in section 6.2.1 exampleB11 with GIL, but this time using the same interpreter version built with GIL disabled:
$> cd /path/to/subfolder/exampleB11/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL disabled
$> python3.14t -m mtguiapp
With the GUI Action taken as the key performance indicator, below observations can be pointed out:
- regardless of its singlethreaded or multithreaded execution (with or without GIL in exampleB11, the performance of the GUI action is almost the same,
- the pre-configured RTE of the framework does not add any significant overhead to the overall program performance.
The code snippet below shows how to start the same program presented in section 6.2.2 exampleB21 with GIL, but this time using the same interpreter version built with GIL disabled:
$> cd /path/to/subfolder/exampleB21/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL disabled
$> python3.14t -m mtguiappThe screenshot below shows the UI of the program with:
-
a GUI action performance of ca. 2.630 [s], which is:
- ca. 110 [ms] faster than its execution with GIL,
- only ca. 100 [ms] slower than the singlethreaded execution of the GUI,
even though there are at least 12 additional (application) tasks runnig,
-
ca. 175 [msg/s] transferred, i.e. (sent or received) messages,
-
exampleB21 with GIL disabled:
The code snippet below shows how to start the same program presented in section 6.2.3 exampleB31 with GIL, but this time using the same interpreter version built with GIL disabled and a relatively larger value of 24 supplied for Fibonacci input numbers N:
$> cd /path/to/subfolder/exampleB31/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL disabled
$> python3.14t -m mtguiapp --fibonacci-input 24 [--force-deficient-frequency]The screenshot below shows the UI of the program with:
-
a GUI action performance of ca. 2.6 [s], which is, as expected, quite close to the performance of the singlethreaded execution of the GUI,
-
exampleB31 with GIL disabled:
The GUI application exampleMPB11 is much like exampleB11, except it additionally creates a few child processes, i.e. instances of class XProcess, each of them requested to calculate Fibonacci(N) for a given number. For this, the target of the created child processes is set to the callback function UserAppUtil.Fibonacci() (explained in section 6.2.3 exampleB31 with GIL):
# file: maintask.py
#...
from xcofdk.fwapi import XProcess
from xuserapp.util.userAppUtil import UserAppUtil
class XFMainTaskMP:
#...
def __StartServices(self):
#...
for ii in range(self.__srvProcCnt):
_srvName = 'ServiceProc{:02d}'.format(ii+1)
_srv = XProcess(UserAppUtil.Fibonacci, name_=_srvName)
self.__lstSrv.append(_srv)
_srv.Start(35+ii)
#...Application modules:
-
mpguiapp.py:
- the main module which uses the related API for pre-configuration of the RTE to disable the subsystem for messaging which is not used,
- it also creates the main task (see below),
-
maintask.py:
- in accordance to the common application design of RC examples, it provides the class
XFMainTaskMP
which represents main task's execution frame derived from the MVC
interface class UserAppControllerIF.
When started, the storyboard of the program is as follows:
- create and start three service processes each to calculate Fibonacci(N) with ca. singlethreaded
execution time in the reference test environment shown in the table below:
Fibonacci input N : CPU time cons. [s] 35 : 1.257 36 : 2.043 37 : 3.310 - keep going with the GUI with regularly checking for XProcess.isTerminated property of each service instance,
- as soon as a service instance is terminated, display its transferred result,
- once all running service instances are terminated, continue with 1. after a short while,
- create and start three service processes each to calculate Fibonacci(N) with ca. singlethreaded
execution time in the reference test environment shown in the table below:
-
userAppUtil.py:
- it provides the utility function UserAppUtil.Fibonacci() used as the callback target of the child processes created by the main task.
- in accordance to the common application design of RC examples, it provides the class
XFMainTaskMP
which represents main task's execution frame derived from the MVC
interface class UserAppControllerIF.
When started, the storyboard of the program is as follows:
Optional command line argument(s):
The available optional command line argument(s) of exampleMPB11 are described in section 6.1.3 Common Program Options.
The code snippet below shows how to start exampleMPB11 via command line:
$> cd /path/to/subfolder/exampleMPB11/
$>
$> # run the application for free-threaded Python 3.14.0 with GIL enabled
$> python3.14 -m mpguiapp The screenshot below shows the UI of the program:
[Top] [Home] [Previous] [Next] – [1. Introduction] [2. Quick Start] [3. Architecture] [4. API Overview] [5. Error Handling] [7. Glossary]
©fasabm 2024-2025 XCOFDK. All Rights Reserved.