[External] : RichTextArea alternative FastCache suggestions
Andy Goryachev
andy.goryachev at oracle.com
Wed Sep 3 15:22:47 UTC 2025
Yes, a system property might be a good compromise since it would allow the developers to customize the behavior without making it a public API. The drawback is that it can only be applied to all the instances at the same time.
-andy
From: Jurgen Doll <jurgen at ivoryemr.co.za>
Date: Wednesday, September 3, 2025 at 05:48
To: Andy Goryachev <andy.goryachev at oracle.com>
Cc: openjfx-discuss at openjdk.org <openjfx-discuss at openjdk.org>
Subject: Re: [External] : RichTextArea alternative FastCache suggestions
I was thinking about a cache size parameter setting maybe, although the current default of 512 is generous,
I was wondering about a really large screen scenario, but then wouldn't the text be larger as well ?
Maybe just have a system property in case ?
On Sep 2 2025, at 9:16 pm, Andy Goryachev <andy.goryachev at oracle.com> wrote:
Dear Jurgen:
Thank you for this suggestion! You are absolutely right noticing that the cache contains ranges as opposed to random indexes.
I admit that the current implementation is less than optimal, but I wanted to avoid iterating over the whole cache. There is also https://bugs.openjdk.org/browse/JDK-8355986 which might have been triggered by me picking a 100 as the default value for the sliding window. Personally, I haven't noticed performance issues on my mac or my windows laptop, have you?
One possibility might be an optimized hash table whose keys encode the starting index of the range. This way we could iterate over the keys, removing the whole buckets. Another alternative is a BTree+ like structure, but that gets complicated rather quickly.
Another thing that did not make it into the incubator was configuration parameters in the constructor. I initially thought there might be a need to fine-tune various internal parameters (such as sliding window size or, in this case, the cache implementation), but it was shot down as unwanted exposure of the implementation details.
I am going to add your comment to the ticket, thanks!
-andy
From: Jurgen Doll <jurgen at ivoryemr.co.za>
Date: Tuesday, September 2, 2025 at 03:46
To: Andy Goryachev <andy.goryachev at oracle.com>, openjfx-discuss at openjdk.org <openjfx-discuss at openjdk.org>
Subject: [External] : RichTextArea alternative FastCache suggestions
Hi Andy
I noticed in VFlow.onContentChange (line 1051) that the whole cellCache is cleared, instead of just the relevant cells that were changed being reset. On reviewing the FastCache implementation I can see the difficulty in why this is the case, so after giving it some thought I have two simple variants of FastCache, that would enable resetting of cells in the cache, for you to consider using instead.
First some background. The current FastCache is great for random data content, however if I'm not mistaken the RichTextArea data that it holds isn't random but rather consists of a range (window) of consecutive cells (paragraphs) from some start index to an end index. These ranges usually move linearly forwards or backwards a single cell at a time (for scrolling), or can jump to either the immediately preceeding or proceeding range (for paging), and sometimes jump to another range completely. It's with this type of data content and behavior that the following two variants are based on.
In the first variant the HashMap is simply dropped and the cache is based solely on the Entry<T> array using the modulo of the index and cache size. This then becomes a circular or ring cache:
public class FastCircularCache<T> {
private static record Entry<T>(int index, T cell) { }
private final Entry<T>[] data;
private final int capacity;
public FastCircularCache(int capacity) {
data = new Entry[capacity];
this.capacity = capacity;
}
public T get(int row) {
Entry<T> entry = data[row % capacity];
if (entry != null && entry.index() == row) {
return entry.cell();
}
return null;
}
/**
* Adds a new cell to the cache. NOTE: this method does not check whether
* another cell for the given row is present, so this call must be preceded by a
* {@link #get(int)}.
*/
public void add(int index, T cell) {
data[index % capacity] = new Entry<>(index, cell);
}
public void reset(int start, int end) {
for (int i = start; i < end; i++) {
data[i % capacity] = null;
}
}
public void clear() {
Arrays.fill(data, null);
}
}
In the second variant the array is dropped instead and the cache is based solely on the HashMap together with min and max int variables, which are the minium and maximum indices present at a particular moment in the HashMap. These are used as eviction indices, where min is evicted from the HashMap if the new index is increasing otherwise max is evicted if the new index is decreasing. This is then a window or sliding cache:
public class FastSlidingCache<T> {
private int min = Integer.MAX_VALUE, max = 0;
private final HashMap<Integer, T> data;
private final int capacity;
public FastSlidingCache(int capacity) {
data = new HashMap<>(capacity);
this.capacity = capacity;
}
public T get(int row) {
if (row < min || row > max) return null;
return data.get(row);
}
/**
* Adds a new cell to the cache. When the cache is full, this method evicts a
* cell from the cache first. NOTE: this method does not check whether
* another cell for the given row is present, so this call must be preceded by a
* {@link #get(int)}.
*/
public void add(int index, T cell) {
if (data.size() >= capacity) {
if (index < min) while (!evict(max--));
if (index > max) while (!evict(min++));
}
if (index < min) min = index;
if (index > max) max = index;
data.put(index, cell);
}
private boolean evict(int index) {
return data.remove(index) != null;
}
public void reset(int start, int end) {
for (int i = start; i < end; i++) {
data.remove(i);
}
}
public void clear() {
min = Integer.MAX_VALUE;
data.clear();
max = 0;
}
}
Each variant has it's strengths and weaknesses. I hope you'll find one of them useful or at least inspiring in improving the current FastCache implementation.
Kind regards
Jurgen
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-discuss/attachments/20250903/f32ebe19/attachment-0001.htm>
More information about the openjfx-discuss
mailing list