javafx.scene.shape.Path (memory) inefficient PathElements
Jim Graham
james.graham at oracle.com
Thu May 25 00:37:46 UTC 2017
Thanks Tom, I've already posted a patch for 8180938 (lazy property creation). Check it out and let me know how it
performs for you.
I have a couple of changes to make to it (and an independent memory usage test to write) before I send it out for formal
review...
...jim
On 5/24/17 3:42 AM, Tom Schindl wrote:
> Hi,
>
> I created:
> - https://bugs.openjdk.java.net/browse/JDK-8180935
> - https://bugs.openjdk.java.net/browse/JDK-8180938
>
> I'll work on a showcase to find out how much memory one can save.
>
> Tom
>
> On 04.05.17 23:33, Jim Graham wrote:
>> Hi Tom,
>>
>> Those look like good suggestions. I would file bugs in JBS and create
>> them separately:
>>
>> - Bug for lazy property creation in path elements
>> - Feature request for lower-memory paths
>>
>> Did you benchmark how much the lazy properties, on their own, would save
>> your application?
>>
>> ...jim
>>
>> On 5/4/17 2:22 PM, Tom Schindl wrote:
>>> Hi,
>>>
>>> We are currently working on a PDF-Rendering library in JavaFX and we
>>> need to draw glyphs using the JavaFX Path API (there are multiple
>>> reasons why we don't use the Text-Node and or Canvas).
>>>
>>> When drawing a page full of Text this means that we have a Path-Object
>>> with gazillions of MoveTo and CubicCurveTo elements who sum up to 30MB
>>> just to represent them in the SG because PathElements store their
>>> information in properties and forcefully intialize them in their
>>> constructor.
>>>
>>> The only public API to work around this problem is to construct a
>>> StringBuffer and use SVGPath which means:
>>> * it takes time to construct the SVG-Path-String
>>> * it takes time to parse the SVG-Path-String in JavaFX
>>>
>>> As an experiment (and because we are still on Java8 we can easily do
>>> that) was that we created our own Shape-Subclass who:
>>> * uses floats (there's no reason to use double in the SG when the
>>> backing API - Path2D - is float based)
>>> * passes the floats directly to the Path2D/NGPath API
>>>
>>> Guess what: We now need 2.5MB / page which means 27.5MB is the overhead
>>> added by the current Path-API - ouch!
>>>
>>> I think a fairly low hanging memory optimization for the PathElement
>>> would be to create properties lazy (only if someone access the property).
>>>
>>> For MoveTo this would mean the following minimal change (eg for the
>>> x-value):
>>>
>>> private DoubleProperty x;
>>> private double _x;
>>>
>>> public final void setX(double value) {
>>> if (x != null) {
>>> xProperty().set(value);
>>> } else {
>>> _x = value;
>>> u();
>>> }
>>> }
>>>
>>> public final double getX() {
>>> return x == null ? _x : x.get();
>>> }
>>>
>>> public final DoubleProperty xProperty() {
>>> if (x == null) {
>>> x = new DoublePropertyBase(_x) {
>>>
>>> @Override
>>> public void invalidated() {
>>> u();
>>> }
>>>
>>> @Override
>>> public Object getBean() {
>>> return MoveTo.this;
>>> }
>>>
>>> @Override
>>> public String getName() {
>>> return "x";
>>> }
>>> };
>>> }
>>> return x;
>>> }
>>>
>>> I guess 99% of the code out there never access the Property so the small
>>> footprint added by the primitive field is justifiable.
>>>
>>> This still has the overhead of all the needless PathElement objects
>>> hanging around so another idea is to have a 3rd SG-Path-Type who
>>> strictly uses the float-Primitives with a similar API to Path2D (in fact
>>> it only acts as a public API to Path2D).
>>>
>>> Thoughts?
>>>
>>> Tom
>>>
>
>
More information about the openjfx-dev
mailing list