Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialization performance improvements #595

Open
mgarin opened this issue Dec 4, 2019 · 10 comments
Open

Initialization performance improvements #595

mgarin opened this issue Dec 4, 2019 · 10 comments

Comments

@mgarin
Copy link
Owner

mgarin commented Dec 4, 2019

I plan to do some big performance-related improvements to the initialization time in one of the future updates, so I wanted to post some information here beforehand.

Currently WebLaF compiles about 1000~1500 styles upon initialization out of which you normally only need about 100~300 in runtime at most and the rest are unused. While memory-wise it's mostly irrelevant (only takes a few megabytes) - it does slow down initialization a lot.

To be more specific - initialization might take up to 5-6 seconds (on Java 6) on slower PCs due to the amount of loaded and compiled styles. And main time out of those 5-6 seconds is compilation of the styles.

While this is partially solved by using newer Java runtime (5-6 seconds become 1-3 seconds on Java 8 and under 1 second on Java 11+) - I constantly add new styles to the base skins which continues to slow down the initialization.

I plan to make all styles compiling lazy - they will only compile once requested by some component. Once compiled they will be cached like they do right now. This change will be a huge performance improvement and will also lift some limitations from SkinExtensions and other parts of the styling system, but I'll talk about that later on once I get these changes done.

@mgarin
Copy link
Owner Author

mgarin commented Dec 26, 2019

A few important changes coming along with this improvement:

1. Skin and skin extensions will now have references to component descriptor classes

This will allow anyone creating any skin or extension to specify custom descriptor for any component they want. Each component descriptor contains all information necessary to read a particular component style.

This will also allow to easily add custom component descriptors within skin or extension. Right now custom descriptors have to be explicitly registered before WebLaF is installed, otherwise skin or extension containing custom component style will throw an exception upon being read.

This change also makes sense because right now descriptors are registered using static methods in StyleManager and applied globally while only being useful (and reasonable) within the particular skin. This can potentially cause errors when you need to load multiple skins with different descriptors for the same component.

Here is an example of how table.xml include will look like after the change:

<skin xmlns="http://weblookandfeel.com/XmlSkin">

    <!-- com.alee.laf.table.WebTable -->
    <descriptor>com.alee.laf.table.TableDescriptor</descriptor>

    <!-- Table -->
    <style type="table">
    ...

</skin>

Syntax might be changed in the final release, but the idea is to keep all descriptors in the related component includes.

Descriptors themselves will probably also contain information required to read related component style (like classes containing XStream annotations).

I will be writing a detailed wiki guide on how component descriptors can be used to modify existing components or to add new custom components with their own styles once the change goes live.

2. Style override for extensions

Due to major changes in how styles are being loaded and compiled - it will be possible to override existing skin styles in extensions. Previously it was disabled due to potential performance impact it would cause when multiple extensions are applied to a large skin.

3. Cleaner API for skins, styles and all related classes

Since I will be making major changes to the internal functioning of the skin loading and usage - I will make sure to provide a cleaner API. It will include foundation for future StyleEditor improvements, code-based (non-XML) styles and potentially css-based (simplified) styles.

@mgarin
Copy link
Owner Author

mgarin commented Dec 27, 2019

1. Component descriptors in skins and extensions

After some back and forth I decided to move descriptors into skin completely, that will simplify things even further and allow direct modifications within the skin without necessity to write any code to be referenced.

Here is current version of the descriptor in XML:

<skin xmlns="http://weblookandfeel.com/XmlSkin">

    <!-- com.alee.laf.table.WebTable -->
    <component type="table" class="javax.swing.JTable">
        <uiBase>com.alee.laf.table.WTableUI</uiBase>
        <ui>com.alee.laf.table.WebTableUI</ui>
        <painterBase>com.alee.laf.table.ITablePainter</painterBase>
        <painter>com.alee.laf.table.TablePainter</painter>
        <defaultStyle>table</defaultStyle>
    </component>

    <!-- Table -->
    <style type="table">
    ...

</skin>

Setting <defaultStyle> will be optional and won't be used in base skins since it would simply be equal to the component type attribute.

@mgarin mgarin modified the milestones: v1.2.12, v1.3.0 Jan 10, 2020
@mgarin
Copy link
Owner Author

mgarin commented Jan 20, 2020

1. Component descriptors in skins and extensions

After testing the XML descriptor approach it has been scrapped due to potential override issues & internal code complexity. Although some parts will be preserved moving on - descriptors will be definable (and redefinable) in the skin (or extension) class itself. This will make them accessible at the style read time and allow users to redefine them at will in their custom skin class if necessary.

It is important to note that while descriptors are very important internally - on practice you barely ever need to change or add them, unless you're making your custom component or want to use a custom UI.

Besides that - I'm continuing my work on the L&F initialization changes and will post more updates as I will be getting closer to some working results.

@mgarin
Copy link
Owner Author

mgarin commented Jan 23, 2020

As a part of this change overall - I'm adjusting internal styles API and it turns out that it will be pretty usable from the code. So this change will also be covering #427

@mgarin mgarin pinned this issue Apr 22, 2020
@mgarin mgarin removed this from the v1.3.0 milestone Apr 23, 2020
@daWoife
Copy link
Contributor

daWoife commented Jun 3, 2020

What do you think, how long will it take to finish this improvement of lazy loading the skin styles.
It would be nice to have a date, is the end of 2020 a possible one ?
Because if I compare the start time of our application running with the old weblaf and the new one, it's currently a too big difference to bring it to our customers.
We only can use the new weblaf if there is a not noticeable start time delay, otherwise we would get some complaints.

@mgarin
Copy link
Owner Author

mgarin commented Jun 8, 2020

I don't think that this change will take all the way up to the end of 2020 to complete, but probably a few more months considering other projects going on right now. Realistically - end of August - mid of September. Although I might be able to finish it earlier if I get a larger window to work on it.

This is still the main thing I'm working on for WebLaF right now, so it's certainly haven't been forgotten. There are just a lot of complications involved as this is the most intricate part of the styling and that's why it cannot be half-assed, otherwise it will require even more work in the future.


Regarding the current loading speed - it bloated mainly due to large amounts of styles being added into the core set, but those aren't all used. In fact - probably only 20 to 30% of them are. So if you're using a custom skin - you can easily cut the loading times by throwing out all the styles you don't actually need.

Also performance vary heavily between Java versions. The higher Java version - the faster the load is. They seem to do a lot of micro-optimization between the versions and it does help a lot.

As for the hardware - loading speed mostly depends on how fast your CPU single core is (for compiling thousands of references in the styles) and slighly on how fast your drive is (for reading multiple XML files from JAR faster).

@daWoife
Copy link
Contributor

daWoife commented Jun 9, 2020

I already thought about throwing out some styles from the xml files, but if August or September is a realistic date I will wait to see the improvements in the loading speed first.
We currently use java webstart to start our applications. Maybe that's not exactly the best way to use the new weblaf. But we don't want to change that, even thinking about switching to icedtee web because of the expiration of java webstart in java 11.
And maybe our customers want to run our applications with AdoptOpenJDK instead of OracleJDK in the future because of the new licensing model of oracle.
And of course in all that possible scenarios the starting time should be nearly as good as it was before. So I'm really curious about the effect of your changes for lazy loading.

@mgarin
Copy link
Owner Author

mgarin commented Jun 11, 2020

Lazy loading will basically bring initial loading time down to the time it takes to read the styles, compilation time will not be there anymore and will be done in runtime on demand. Even with big applications - only the styles required at the exact moment will be compiled - which will basically mean that compile time won't even be noticeable in most cases and would only take a couple of milliseconds.

As for specifics - read vs compile right now is about 25% vs 75% of the initialization time taken, 300~500ms for reading styles, 1200~4000ms for compiling, depending on various aspects (Java version, drive type, CPU core performance, etc).

Reading still takes a lot of time, but I don't think there is a reasonable way to reduce that as of now. I might look into going away from XStream in the future in favor of a custom XML reader designed specifically for the styling, but even then the time won won't be as big.

@charghet
Copy link

At present, not only the initialization time is long, but also the memory consumption is high. When I run a simple example, only a WebFrame and a WebButton occupy 200MB of memory, which means that when I run multiple small programs that use WebLAF, that will take up a lot of memory. Can you make improvements in this respect?

@mgarin
Copy link
Owner Author

mgarin commented Aug 21, 2020

@charghet
The actual memory footprint of WebLaF's UI is higher than in most other L&Fs because it uses separate UI instances for each component and keeps a lot of other information in memory in runtime. But it is still quite low and shouldn't cause any issues. You can easily test this on WebLaF demo application:

image

I've opened a bunch of examples in this case, including some heavy ones, and application only takes 42~46 mb of memory (it's hard to get the example to show it, but you can see it by pressing the memory button to GC).

This is the demo I downloaded and ran from the most recent release:
https://github.com/mgarin/weblaf/releases/download/v1.2.13/weblaf-demo-1.2.13-jar-with-dependencies.jar

It will of course vary depending on your JVM version and OS, but it should be fairly consistent.


Important note - if you're looking at the memory that you java.exe process is using - that is not the memory application actually occupies. That is the memory that JVM allocated for the use. It can easily be 800 mb or 10 gb (on 64-bit JVM at least) if your JVM is configured to be able to take as much.

As long as you haven't configured memory size available to JVM - it will act according to it's default settings. From my experience - older JVMs might be easily using up to a gigabyte of memory. New ones - for example Java 13 that I ran demo on for the screenshot above - seem to be more careful with memory allocation.

Either way, in case you aren't familiar with memory usage in Java - I recommend reading some articles about it.


And in case using -Xmx and -Xms JVM options doesn't help you - I'll need a code example that reproduces this issue for you, as well as exact Java and OS version you used to run it - so that I can debug it.

I tried running "only a WebFrame and a WebButton" plus WebMemoryBar to see exact memory usage:

public class MemoryTest
{
    public static void main ( final String[] args )
    {
        SwingUtilities.invokeLater ( new Runnable ()
        {
            @Override
            public void run ()
            {
                WebLookAndFeel.install ();

                final WebFrame frame = new WebFrame ();
                frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );

                frame.setLayout ( new FlowLayout () );
                frame.add ( new WebButton ( "Button" ) );
                frame.add ( new WebMemoryBar () );

                frame.pack ();
                frame.setLocationRelativeTo ( null );
                frame.setVisible ( true );
            }
        } );
    }
}

Result after running on JDK 1.8.0 u211:

image

So yes, application did allocate ~300mb of memory, but only actually used ~12mb.
From my experience the actual footprint of all WebLaF light skin styles is about 5-6mb.
The rest is the components, frame, image used for drawing the frame and some other runtime objects.

It is easily possible to reduce the allocated size by setting -Xmx and -Xms JVM options.
Here is an example using -Xms20m -Xmx20m:

image

The actual java.exe process still takes more memory:

image

But that is the overall memory your application uses including memory that JVM allocated for itself and while it can also be further tuned with other JVM options - it might also depend on JVM version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants