Friends of OpenJDK Today

FEPCOS-J (3) – Build native executables of Java-coded networked systems

December 13, 2023

Author(s)

  • Avatar photo
    Gerhard Fuchs

    Gerhard is a computer scientist who has programmed in Java since the year 2000. Currently, he works independently on the FEPCOS-project, which aims to simplify the programming of networked systems. ... Learn more

FEPCOS-J prototypically enables Java developers to realize networked systems without low-level network programming and to automatically build native executables by means of GraalVM. This post introduces the concept and gives you an example by rebuilding the source code explained in my previous posts.

Please help me to make FEPCOS-J a Free/Libre and Open-Source Software (FLOSS).

Introduction

FEPCOS-J [1] enables a Java developer to declaratively program networked systems and compose them like building blocks [2]. For this purpose, FEPCOS-J provides Java modules and generates code to free a Java developer from network programming [3].

In brief, the developer specifies a system by using the annotations FEPCOS-J offers. Afterwards, the developer uses the FEPCOS-J Processor fjp to process the specification, generate code, and build two modular Jar files: a system export module and a system import module.

To explain, combined with the FEPCOS-J Exporter fjx, the system export module is the Java application of a multithreaded server that provides access to the specified system via an IPv4 network. Further, Java-coded system users can both blockingly and concurrently access the system via the IPv4 network by using the system import module.

GraalVM is another technology that processes Java applications to build standalone binaries that are smaller, start up faster, offer peak performance without a warmup, and use less memory and CPU compared to applications running on a Java Virtual Machine [4].

For this purpose, GraalVM provides the native-image tool [5]. In particular, native-image can convert modularized Java applications into native executables when invoked with the correct parameters tailored to the application.

This post explains the prototypical interaction of FEPCOS-J and GraalVM, which enables Java developers to automatically build native executables of declaratively programmed networked systems.

It introduces how FEPCOS-J uses GraalVM to build native executables and provides an example afterwards. Finally, the post evaluates the build process and draws a conclusion.

I would also like to ask you to help me to make FEPCOS-J a Free/Libre and Open-Source Software (FLOSS).

FEPCOS-J builds native executables by using GraalVM

In its current prototypical state, the FEPCOS-J processor fjp accepts the parameter --native, as follows:

fjp --native

This command causes fjp to build the default output and a native executable of the system export module by invoking native-image with application-tailored parameters afterwards.

As a result, this command builds a native executable of a multithreaded server, which provides access to the capabilities of a declaratively programmed system via an IPv4 network.

To point out, the developer does not have to care about the network programming, as FEPCOS-J provides Java modules and generates additional required code for him.

The remaining section delineates the interaction between FEPCOS-J and GraalVM using the OpenJDK-based Community Edition for JDK 17 [6].

Building native executables with GraalVM

There are various ways to build native executables with GraalVM, in particular by using the following command:

native-image -o <name> -p <module_path> -m <module>/<main_class>

To explain, -o <name> specifies the name of the native executable, and -p <module_path> specifies the module path, which must contain all modules of the application to be built.

Further, -m <module>/<main_class> specifies the module and the class, which contains the main(…) method.

To sum up, native-image can build native executables with a definable name out of a Java module, provided that the name, the module path, and the main class are properly specified as parameters.

The modular design of FEPCOS-J allows the specification of the required native-image parameters

In order to set the above-mentioned parameters of native-image, it is necessary to know the required modules and module paths of a networked system developed with FEPCOS-J. Further, it is necessary to know its main class.

In brief, assuming a system sys is implemented in the project directory ${PROJ}, then

  • the module path:
    • must include ${FEPCOS_HOME}/mlib with modules FEPCOS-J provides as modular Jar files;
    • must include ${PROJ}/tgt, the fjp-generated directory, which in particular contains the system export module sys.exp.jar;
    • may include ${PROJ}/mlib, which must contain all optional system-specific modular Jar files required to build the system export module sys.exp.jar and run fjx.
  • the main class is the fjp-generated class sys.exp/sys.exp.Main packed in sys.exp.jar.

Fig. 1 and Tabs. 1-3 provide detailed information: The figure depicts the module dependencies and module paths, and the tables explain the involved modules.

Module dependencies of networked systems created with FEPCOS-J: The realization of a system sys within a project directory ${PROJ} and its subdirectories src, mlib, and tgt requires modules FEPCOS-J provides in the directory ${FEPCOS_HOME}/mlib. It is not shown that all modules require java.base. This is taken for granted.
Fig. 1) Module dependencies of networked systems created with FEPCOS-J: The realization of a system sys within a project directory ${PROJ} and its subdirectories src, mlib, and tgt requires modules FEPCOS-J provides in the directory ${FEPCOS_HOME}/mlib. It is taken for granted that all modules require java.base, thus not shown.

Modules FEPCOS-J provides

FEPCOS-J provides the following modules as modular Jar files in the directory ${FEPCOS_HOME}/mlib.

Module NameExplanation
fepcos.annotationsThis module contains Java annotations that allow Java developers to declaratively compose networked systems. It is solely required for the programming and processing of the system specification. Thus, the module is statically required.
fepcos.expThis module contains the Java classes required by a system export module to provide access to a system within an IPv4 network. In particular, it contains the generic part of the FEPCOS-J Exporter fjx, which is essentially a lightweight multithreaded server.
fepcos.impThis module contains an abstract system interface required by a system import module to access a system via an IPv4 network.
fepcos.coreThis module contains the core aspects of FEPCOS-J, in particular the network protocol.
Tab. 1) Modules FEPCOS-J provides.

Modules the Java developer provides

A Java developer programs the system specification sys.spec within the directory ${PROJ}/src. Further, the optional directory ${PROJ}/mlib may contain modular jar files of system dependencies sys-deps.

Module NameExplanation
sys.specThis module contains the system specification of a system called sys. In brief, it contains the Java-code programmed by the developer, as explained in my previous posts [2,3].
sys-depsThe system dependencies are an optional collection of modular Jar files. They contain every module FEPCOS-J does not provide but needs to build and execute sys.spec. It is important to realize that if sys.spec requires sys-deps, then sys.exp requires them, too.
Tab. 2) Modules the Java developer provides.

Modules generated by fjp

The FEPCOS-J Processor fjp generates the following modules and stores them as modular Jar files in the directory ${PROJ}/tgt.

Module NameExplanation
sys.expThis module is the fjp-generated system export module. It includes all system-specific Java classes that provide access to the specified system via an IPv4 network. In particular, it contains the generated class sys.exp.Main that has a main(…) method, which starts the correctly parametrized FEPCOS-J Exporter. For the sake of completeness, fjx is a start script that calls java, specifying the module path and the main class sys.exp.Main.
sys.impThis module is the fjp-generated system import module. It contains a generated system interface that allows users to access the specified system both blockingly and concurrently.
Tab. 3) Modules generated by fjp.

Interaction of FEPCOS-J and GraalVM to build native executables

Build native executables manually

Combining the requirements of native-image with the properties of FEPCOS-J leads to the following commands to be executed within ${PROJ}:

Firstly,

fjp

generates the system import module and the system export module out of the system specification.

Secondly,

MP=${FEPCOS_HOME}/mlib:tgt[:mlib]

specifies the module path ${MP}. The directory mlib is optional and is only appended to the module path if the system specification requires further modules. Finally,

native-image -o sys.exp -p ${MP} -m sys.exp/sys.exp.Main

generates the native executable sys.exp out of the system export module. So, the native executable and the system export module have the same name.

Build native executables automatically

At this point, it is important to realize that fjp is a start script that can check if the --native parameter is set. Thus, it is possible to automate the manual build process as follows:

Firstly, fjp calls java with the correct parameters so that it runs the FEPCOS-J Processor. If the --native parameter is set, fjp runs native-image, as explained above, afterwards.

Exemplary usage of FEPCOS-J to build native executables for Java-coded networked systems

My previous posts [2,3] explain how to use FEPCOS-J to declaratively program networked systems and how to compose them like building blocks.

This section takes up the example shown there and illustrates its realization with native executables. For a detailed description of the source code and development workflow, see my previous posts.

Example scenario

The following recaps the example scenario and provides the names of the involved computers and native executables, in addition. Fig. 2 provides an illustration.

Example scenario realized by using FEPCOS-J: Four computers named adm, box, lumo, and trike native executable applications communicating via two different IPv4 networks, 10.0.0.0/24 and 10.1.0.0/24. In detail, adm runs app; box runs calculator.exp; lumo runs adder.exp; and trike runs multiplier.exp. Further, app accesses calculator.exp via internet socket 10.0.0.1:8001, and calculator.exp accesses adder.exp via 10.1.0.1:8001 and multiplier.exp via 10.1.0.2:8001.
Fig. 2) Example scenario realized by using FEPCOS-J: The computers named adm, box, lumo, and trike run the native executables app, calculator.exp, adder.exp, and multiplier.exp, as noted under their names.

Four computers named lumo, trike, box and adm communicate via the two IPv4 networks, 10.0.0.0/24 and 10.1.0.0/24, to run native executable applications:

  • Firstly, lumo runs adder.exp, which provides the capability to add two numbers via internet socket 10.1.0.1:8001.
  • Secondly, trike runs multiplier.exp, which provides the capability to multiply two numbers via internet socket 10.1.0.2:8001.
  • Thirdly, box runs calculator.exp, which provides both the capability to add and multiply via internet socket 10.0.0.1:8001. To clarify, calculator.exp delegates requests to adder.exp or multiplier.exp, respectively, via the IPv4 network 10.1.0.0/24.
  • Finally, adm runs app, which accesses the capabilities of calculator.exp via the IPv4 network 10.0.0.0/24.

To sum up, lumo and trike act as multithreaded servers, adm acts as a client, and box acts as both a multithreaded server and a client.

Build and run the example scenario

My last post [2] explains in detail how to use FEPCOS-J to realize the example scenario by building Java applications. The following describes how to rebuild the scenario with native executables and run them.

In short, the development workflow is almost identical, solely differing in that:

  • the calling of fjp --native substitutes the calling of fjp;
  • the running of fjx with the generated system export module is replaced by the running of the generated native executable;
  • a native executable of the system user app is manually built and started.

Figs. 3-6 depict screenshots of the native builds, including details. You will see that the outputs of fjp --native and native-image are piped to a file for evaluation afterwards.

Build and run adder.exp

Running the native executable adder.exp built by using FEPCOS-J: Calling fjp --native within the project directory causes the execution of native-image, which generates the native executable adder.exp. Further, the file adder.out contains the piped output for subsequent evaluation. Running adder.exp provides access to the system internet socket 10.1.0.1:8001, which is specified as a parameter.
Fig. 3) Running the native executable adder.exp built by using FEPCOS-J: Calling fjp --native within the project directory (1) causes the execution of native-image, which generates the native executable adder.exp (2). Further, the file adder.out contains the piped output for subsequent evaluation. Running adder.exp (3) provides access to the system internet socket 10.1.0.1:8001, which is specified as a parameter.

Build and run multiplier.exp

Running the native executable multiplier.exp built by using FEPCOS-J: Calling fjp --native within the project directory causes the execution of native-image, which generates the native executable multiplier.exp. Further, the file multiplier.out contains the piped output for subsequent evaluation. Running multiplier.exp provides access to the system internet socket 10.1.0.2:8001, which is specified as a parameter.
Fig. 4) Running the native executable multiplier.exp built by using FEPCOS-J: Calling fjp --native within the project directory (1) causes the execution of native-image, which generates the native executable multiplier.exp (2). Further, the file multiplier.out contains the piped output for subsequent evaluation. Running multiplier.exp (3) provides access to the system internet socket 10.1.0.2:8001, which is specified as a parameter.

Build and run calculator.exp

Running the native executable calculator.exp built by using FEPCOS-J: Firstly, scp copies adder.imp.jar and multiplier.imp.jar, which are the previously built system import modules of calculator's parts, into the subdirectory mlib. Calling fjp --native within the project directory causes the execution of native-image, which generates the native executable calculator.exp. Further, the file calculator.out contains the piped output for subsequent evaluation. Running calculator.exp provides access to the system internet socket 10.0.0.1:8001, which is specified as a parameter.
Fig. 5) Running the native executable calculator.exp built by using FEPCOS-J: Firstly, scp copies adder.imp.jar and multiplier.imp.jar, which are the previously built system import modules of calculator's parts, into the subdirectory mlib. Calling fjp --native within the project directory (1) causes the execution of native-image, which generates the native executable calculator.exp (2). Further, the file calculator.out contains the piped output for subsequent evaluation. Running calculator.exp (3) provides access to the system internet socket 10.0.0.1:8001, which is specified as a parameter.

Build and run app

Build and run the system user app: Firstly, scp copies the system import module calculator.imp.jar into the subdirectory mlib. After compiling the application with javac, native-image processes the class files. As a result, app is the generated native executable. Further, the file app.out contains the piped output for subsequent evaluation. Running app causes the expected behavior.
Fig. 6) Build and run the system user app: Firstly, scp copies the system import module calculator.imp.jar into the subdirectory mlib. After compiling the application with javac, native-image processes the class files (1). As a result, app is the generated native executable (2). Further, the file app.out contains the piped output for subsequent evaluation. Running app (3) causes the expected behavior.

Evaluation

This section evaluates the exemplary usage of FEPCOS-J to build native executables of Java-coded networked systems based on the above-mentioned and below-listed output files adder.out, multiplier.out, calculator.out, and app.out.

Raw outputs

As can be seen below, all output files solely contain native-image build output [7].

========================================================================================================================
GraalVM Native Image: Generating 'adder.exp' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                   (44,3s @ 0,07GB)
 Java version: 17.0.8+7, vendor version: GraalVM CE 17.0.8+7.1
 Graal compiler: optimization level: 2, target machine: armv8-a
 C compiler: gcc (linux, aarch64, 10.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [***]                                                                    (227,8s @ 0,28GB)
   2.996 (72,45%) of  4.135 types reachable
   3.662 (52,01%) of  7.041 fields reachable
  13.633 (44,38%) of 30.717 methods reachable
     931 types,     0 fields, and   352 methods registered for reflection
      59 types,    58 fields, and    53 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                              (35,3s @ 0,34GB)
[4/8] Parsing methods...      [*****]                                                                   (21,5s @ 0,31GB)
[5/8] Inlining methods...     [***]                                                                     (18,2s @ 0,20GB)
[6/8] Compiling methods...    [**************]                                                         (205,4s @ 0,32GB)
[7/8] Layouting methods...    [****]                                                                    (12,6s @ 0,34GB)
[8/8] Creating image...       [***********]                                                            (130,7s @ 0,31GB)
   4,41MB (36,62%) for code area:     7.757 compilation units
   7,06MB (58,68%) for image heap:   90.061 objects and 5 resources
 579,61kB ( 4,70%) for other data
  12,04MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3,35MB java.base                                            1,01MB byte[] for code metadata
 786,86kB svm.jar (Native Image)                             902,53kB java.lang.String
 108,84kB java.logging                                       875,48kB byte[] for general heap data
  55,80kB org.graalvm.nativeimage.base                       692,41kB java.lang.Class
  27,03kB fepcos.exp                                         673,79kB byte[] for java.lang.String
  23,27kB org.graalvm.sdk                                    348,66kB java.util.HashMap$Node
  18,14kB jdk.internal.vm.ci                                 257,47kB com.oracle.svm.core.hub.DynamicHubCompanion
   6,31kB jdk.internal.vm.compiler                           170,81kB java.lang.String[]
   2,56kB fepcos.core                                        162,27kB java.lang.Object[]
   1,86kB jdk.proxy1                                         148,84kB byte[] for embedded resources
   4,75kB for 4 more packages                                  1,19MB for 843 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                       84,5s (11,9% of total time) in 169 GCs | Peak RSS: 0,79GB | CPU load: 3,00
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/fuchs/demo/adder/adder.exp (executable)
========================================================================================================================
Finished generating 'adder.exp' in 11m 41s.
========================================================================================================================
GraalVM Native Image: Generating 'multiplier.exp' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                   (69,4s @ 0,09GB)
 Java version: 17.0.8+7, vendor version: GraalVM CE 17.0.8+7.1
 Graal compiler: optimization level: 2, target machine: armv8-a
 C compiler: gcc (linux, aarch64, 10.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [***]                                                                    (347,5s @ 0,30GB)
   2.996 (72,45%) of  4.135 types reachable
   3.662 (52,01%) of  7.041 fields reachable
  13.633 (44,39%) of 30.715 methods reachable
     931 types,     0 fields, and   352 methods registered for reflection
      59 types,    58 fields, and    53 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                              (46,0s @ 0,27GB)
[4/8] Parsing methods...      [******]                                                                  (41,6s @ 0,27GB)
[5/8] Inlining methods...     [***]                                                                     (23,1s @ 0,30GB)
[6/8] Compiling methods...    [*****************]                                                      (311,4s @ 0,38GB)
[7/8] Layouting methods...    [*********]                                                               (80,1s @ 0,36GB)
[8/8] Creating image...       [********]                                                                (65,9s @ 0,36GB)
   4,41MB (36,62%) for code area:     7.756 compilation units
   7,06MB (58,68%) for image heap:   90.078 objects and 5 resources
 579,83kB ( 4,70%) for other data
  12,04MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3,35MB java.base                                            1,01MB byte[] for code metadata
 786,86kB svm.jar (Native Image)                             902,56kB java.lang.String
 108,84kB java.logging                                       875,48kB byte[] for general heap data
  55,80kB org.graalvm.nativeimage.base                       692,41kB java.lang.Class
  26,98kB fepcos.exp                                         673,87kB byte[] for java.lang.String
  23,27kB org.graalvm.sdk                                    349,41kB java.util.HashMap$Node
  18,14kB jdk.internal.vm.ci                                 257,47kB com.oracle.svm.core.hub.DynamicHubCompanion
   6,31kB jdk.internal.vm.compiler                           170,82kB java.lang.String[]
   2,56kB fepcos.core                                        162,27kB java.lang.Object[]
   1,86kB jdk.proxy1                                         148,84kB byte[] for embedded resources
   4,77kB for 4 more packages                                  1,19MB for 841 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                       87,5s (8,7% of total time) in 157 GCs | Peak RSS: 0,75GB | CPU load: 3,18
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/fuchs/demo/multiplier/multiplier.exp (executable)
========================================================================================================================
Finished generating 'multiplier.exp' in 16m 32s.
========================================================================================================================
GraalVM Native Image: Generating 'calculator.exp' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
[1/8] Initializing...                                                                                   (13,8s @ 0,13GB)
 Java version: 17.0.8+7, vendor version: GraalVM CE 17.0.8+7.1
 Graal compiler: optimization level: 2, target machine: compatibility
 C compiler: gcc (linux, x86_64, 10.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [***]                                                                     (54,9s @ 0,53GB)
   3.053 (72,73%) of  4.198 types reachable
   3.781 (52,17%) of  7.247 fields reachable
  13.920 (44,75%) of 31.109 methods reachable
     942 types,     0 fields, and   360 methods registered for reflection
      61 types,    61 fields, and    55 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (8,7s @ 0,50GB)
[4/8] Parsing methods...      [***]                                                                      (5,7s @ 0,49GB)
[5/8] Inlining methods...     [***]                                                                      (4,2s @ 0,85GB)
[6/8] Compiling methods...    [*******]                                                                 (45,0s @ 0,41GB)
[7/8] Layouting methods...    [**]                                                                       (3,4s @ 0,57GB)
[8/8] Creating image...       [***]                                                                      (6,1s @ 0,72GB)
   4,72MB (37,94%) for code area:     7.965 compilation units
   7,10MB (57,03%) for image heap:   91.985 objects and 5 resources
 641,44kB ( 5,03%) for other data
  12,45MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3,63MB java.base                                            1,06MB byte[] for code metadata
 777,44kB svm.jar (Native Image)                             916,22kB java.lang.String
 111,98kB java.logging                                       883,68kB byte[] for general heap data
  61,92kB org.graalvm.nativeimage.base                       706,45kB java.lang.Class
  29,95kB fepcos.exp                                         683,27kB byte[] for java.lang.String
  23,82kB jdk.internal.vm.ci                                 381,14kB java.util.HashMap$Node
  23,10kB org.graalvm.sdk                                    262,37kB com.oracle.svm.core.hub.DynamicHubCompanion
   7,93kB fepcos.imp                                         173,63kB java.lang.String[]
   6,10kB jdk.internal.vm.compiler                           162,95kB java.lang.Object[]
   5,34kB calculator.exp                                     149,16kB java.util.HashMap$Node[]
   9,29kB for 7 more packages                                  1,20MB for 854 more object types
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                        4,5s (3,1% of total time) in 41 GCs | Peak RSS: 1,36GB | CPU load: 3,61
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/fuchs/demo/calculator/calculator.exp (executable)
========================================================================================================================
Finished generating 'calculator.exp' in 2m 23s.
========================================================================================================================
GraalVM Native Image: Generating 'app' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
[1/8] Initializing...                                                                                   (15,6s @ 0,11GB)
 Java version: 17.0.8+7, vendor version: GraalVM CE 17.0.8+7.1
 Graal compiler: optimization level: 2, target machine: compatibility
 C compiler: gcc (linux, x86_64, 10.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [***]                                                                     (70,6s @ 0,36GB)
   3.023 (72,51%) of  4.169 types reachable
   3.731 (51,82%) of  7.200 fields reachable
  13.821 (44,57%) of 31.009 methods reachable
     938 types,     0 fields, and   360 methods registered for reflection
      61 types,    61 fields, and    55 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (8,9s @ 0,31GB)
[4/8] Parsing methods...      [***]                                                                      (7,3s @ 0,38GB)
[5/8] Inlining methods...     [***]                                                                      (5,4s @ 0,29GB)
[6/8] Compiling methods...    [*******]                                                                 (57,5s @ 0,32GB)
[7/8] Layouting methods...    [**]                                                                       (4,1s @ 0,48GB)
[8/8] Creating image...       [***]                                                                      (7,2s @ 0,62GB)
   4,66MB (37,70%) for code area:     7.896 compilation units
   7,09MB (57,33%) for image heap:   91.022 objects and 5 resources
 629,94kB ( 4,97%) for other data
  12,37MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3,61MB java.base                                            1,05MB byte[] for code metadata
 777,32kB svm.jar (Native Image)                             910,78kB java.lang.String
 111,98kB java.logging                                       872,35kB byte[] for general heap data
  61,92kB org.graalvm.nativeimage.base                       699,80kB java.lang.Class
  23,82kB jdk.internal.vm.ci                                 680,40kB byte[] for java.lang.String
  23,10kB org.graalvm.sdk                                    359,53kB java.util.HashMap$Node
   7,93kB fepcos.imp                                         259,79kB com.oracle.svm.core.hub.DynamicHubCompanion
   6,10kB jdk.internal.vm.compiler                           172,77kB java.lang.String[]
   2,44kB fepcos.core                                        162,59kB java.lang.Object[]
   2,25kB app                                                148,84kB byte[] for embedded resources
   6,18kB for 5 more packages                                  1,20MB for 850 more object types
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                        9,0s (5,0% of total time) in 64 GCs | Peak RSS: 1,16GB | CPU load: 3,64
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/fuchs/demo/app/app (executable)
========================================================================================================================
Finished generating 'app' in 2m 58s.

Prepared data

Tab. 4 maps selected data from the raw outputs to the name of the executing computer. Tab. 5 shows the technical aspects of these computers.

Further, the size of the code areas is 4,41-4,72 MiB; fepcos.exp contributes 26,98-29,95 kiB; fepcos.imp contributes 7,93 kiB; fepcos.core contributes 2,44-2,56 kiB; calculator.exp contributes 5,34 kiB; and app contributes 2,25 kiB.

In other words, all required FEPCOS-J modules together contribute less than 1% to the code area, and the fjp-generated code even less.

OutputExecutableSize / MiBDurationComputer
adder.outadder.exp12,0411m 41slumo
multiplier.outmultiplier.exp12,0416m 32strike
calculator.outcalculator.exp12,452m 23sbox
app.outapp12,372m 58samd
Tab. 4) Selected parameters gained from building the example scenario: Output = name of the output file; Executable = name of the generated native executable; Size / MiB = size of the generated native executable in MiB = 220 bytes; Duration = duration required by native-image to build the native executable; Computer = name of the executing computer.
ComputerCPURAMOperating system
lumo4 x 1.2GHz Broadcom BCM28371 GBlinux, aarch64
trike4 x 1.2GHz Broadcom BCM2837 1 GBlinux, aarch64
box4 x 1.1GHz Intel® Celeron® N34508 GBlinux, x86_64
adm4 x 2.16GHz Intel® Pentium® N35404 GBlinux, x86_64
Tab. 5) Technical aspects of the current test environment for FEPCOS-J: lumo and trike are each a Raspberry Pi® Model 3B; box is a mini PC; adm is a notebook.

Lessons learned

The rebuild of the example scenario showed that:

  • The prototype of FEPCOS-J and GraalVM can interact, such that fjp --native starts native-image with the correct parameters.
  • It is consequently possible to use FEPCOS-J to automatically build native executables for Java-coded networked systems, in particular multithreaded servers.
  • The building of the native executable on the x86_64 computers took 2m 23s or 2m 58s, respectively.
  • The building of the native executable on the aarch64 computers took 11m 41s or 16m 32s, respectively.
  • The size of the native executables was 12,04 -12,45 MiB.
  • The size of the code areas that are part of the native executables was 4,41 - 4,72 MiB.
  • Thus, the code footprint of all required FEPCOS-J modules together was less than 1% of the code area, and the fjp-generated code was even less.

Conclusion

This post explained the prototypical interaction of FEPCOS-J and GraalVM. It introduced the concept and provided an example by rebuilding the scenario from my last post [2] afterwards. Finally, it presented an evaluation of the GraalVM native-image outputs.

FEPCOS-J enables Java developers to declaratively program networked systems and compose them like building blocks by providing Java modules and generating code, which frees the developer from network programming [2,3].

GraalVM is another technology that provides the native-image tool [5] to convert Java applications into native executables.

As shown in this post, the prototype of FEPCOS-J can interact with native-image, automatically building native executables of Java-coded networked systems, in particular multithreaded servers. Further, the native-image outputs gave a first impression of the build times and sizes of the native executables.

To sum up, FEPCOS-J prototypically enables Java developers to realize networked systems without low-level network programming and to automatically build native executables by means of native-image.

I need your help to make FEPCOS-J a Free/Libre and Open-Source Software (FLOSS)

Have you thought about how FEPCOS-J can support your work?

FEPCOS-J shall become a Free/Libre and Open Source Software (FLOSS).

So far, FEPCOS-J is a prototype that I have independently developed. Now, I need your support, especially regarding legal issues, the testing environment, and funding.

Please let me know what you think about it and provide me with any feedback. Detailed contact information is available on my web page [8].

Thanks for reading!

References:

  1. fepcos.info: "FEPCOS-J"; http://fepcos.info/en/fepcos-j.html (last accessed: 2023-12-13).
  2. Gerhard Fuchs: "FEPCOS-J (2) – Declaratively compose networked systems in Java"; https://foojay.io/today/fuchs-2023-fepcos-j-02/ (last accessed: 2023-12-13).
  3. Gerhard Fuchs: "FEPCOS-J (1) – Description, Impressions of Usage, Current State"; https://foojay.io/today/fuchs-2023-fepcos-j-01/ (last accessed: 2023-12-13).
  4. Oracle and/or its affiliates: "GraalVM Overview"; https://www.graalvm.org/latest/docs/introduction/ (last accessed: 2023-12-13).
  5. Oracle and/or its affiliates: "Native Image"; https://www.graalvm.org/latest/reference-manual/native-image/ (last accessed: 2023-12-13).
  6. Mohamed Ez-zarghili: "GraalVM for JDK 17 Community 17.0.8"; https://github.com/graalvm/graalvm-ce-builds/releases/tag/jdk-17.0.8 (last accessed: 2023-12-13).
  7. Oracle and/or its affiliates: "Native Image Build Output"; https://www.graalvm.org/jdk17/reference-manual/native-image/overview/BuildOutput/ (last accessed: 2023-12-13).
  8. fepcos.info: "Gerhard Fuchs"; http://fepcos.info/en/fuchs.html (last accessed: 2023-12-13).
Topics:

Related Articles

View All

Author(s)

  • Avatar photo
    Gerhard Fuchs

    Gerhard is a computer scientist who has programmed in Java since the year 2000. Currently, he works independently on the FEPCOS-project, which aims to simplify the programming of networked systems. ... Learn more

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard