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

Optimising CubeWatchers unloading #365

Merged
merged 1 commit into from
Apr 28, 2018

Conversation

Foghrye4
Copy link
Contributor

@Foghrye4 Foghrye4 commented Apr 22, 2018

Working marvelously.
Imgur

Copy link
Member

@Barteks2x Barteks2x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, just a few things to improve

public T next() {
prev = next;
next = null;
return prev;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assumes hasNext has been called. Not a problem for most use cases but could be susprising to discover that if (!thing.isEmpty()) { return thing.iterator().next(); } doesn't work.

/** Contain all data in sorting order */
private final List<T> data = new ArrayList<T>();
/** Contain all data without certain order. Used to detect if element is already added */
private final Set<T> data2 = new HashSet<T>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to dataAsSet

/** Sort and remove scheduled elements */
@SuppressWarnings({"unchecked", "rawtypes"})
public void tick() {
T[] a = (T[]) data.toArray();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using 2 ObjectArrayLists from fastutils, and swapping them around. FastUtil exposes the internal array from it's array list.

@Barteks2x
Copy link
Member

Barteks2x commented Apr 22, 2018

import java.util.function.Predicate;

/**
* Helper class to delay removing of an elements to sorting tick. Wrap both ArrayList and HashSet classes inside of it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain why in the comment

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from bcb306e to 57f60bc Compare April 24, 2018 17:50
@Foghrye4
Copy link
Contributor Author

I decide to use a raw array of objects as data container. This way we can remove elements right during iterations.

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch 2 times, most recently from 2c8eba0 to 753ef1c Compare April 24, 2018 18:38
Arrays.sort((T[])data, 0, size, order);
}

/** Check if list is empty. Even when it return true list still could contain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is that weird javadoc formatting?

}
Object old = data[index];
data[index] = element;
this.add((T) old);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that behavior actually useful in any way? It's likely nothing actually needs to insert at specific position in the array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function used here:

cubesToGenerate.add(0, playerInstance);

To add cube to generation query with high priority.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And your code completely missed the point as when a second none is added the first one will be moved. To the end. Just make the values stored internally in reverse order so you can have insertFirst method and it would only need to append it at the end of the array.

}

private void grow() {
data = Arrays.copyOf(data, data.length << 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally don't have an issue wit bitshifts when they are actually useful but here it only obfuscates the code for people who don't know much about them

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from 753ef1c to 9b7ace3 Compare April 25, 2018 17:40
@Foghrye4
Copy link
Contributor Author

Considering adding elements to the start of a list I have even better idea.

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from 9b7ace3 to 9b3cc10 Compare April 25, 2018 17:45
Copy link
Member

@Barteks2x Barteks2x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor corrections, and when you are done, run profiler/sampler again and show the results.,

* @return if list contain any accessible data
*/
public boolean isEmpty() {
return size - toRemove.size() == 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to add assert size - toRemove.size() >= 0; (my genIntellijRuns task enables assertions by default)


private void peekNext() {
while (next == null && a < start + size) {
T e = (T) data[++a - 1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either split it into 2 lines or use increment properly and do data[i++]

T e = (T) data[++a - 1];
if (toRemove.remove(e)) {
dataAsSet.remove(e);
data[--a] = data[start + --size];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There you should definitely split it into more lines. Just because it works doesn't mean it's a good idea.

@Override
public void remove() {
dataAsSet.remove(prev);
data[--a] = data[start + --size];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

if (predicate.test(a)) {
toRemove.remove(a);
dataAsSet.remove(a);
data[i] = data[start + --size];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

throw new NullPointerException("This list does not allow null elements.");
if (start <= 0)
grow();
data[--start] = element;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again

* to remove non-existing elements will cause nasty bugs.
*/
public void remove(T entry) {
toRemove.add(entry);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does it handle removing element that doesn't exist in the list? I'm pretty sure it breaks size and isEmpty. If you don't want to handle such things, at least crash in those cases (PlayerCubeMap shouldn't be doing that). Same for other strange cases like adding the same thing twice.

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from 9b3cc10 to 44e2106 Compare April 25, 2018 18:19
@Foghrye4
Copy link
Contributor Author

Done. No noticeable anything. Snapshot created after flying straight forward in creative mode for a few minutes.
Imgur

@Barteks2x
Copy link
Member

This is running with vanilla world, not cubic chunks world.

@Foghrye4
Copy link
Contributor Author

Ehm...
Imgur

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch 3 times, most recently from 1b741c7 to cecb80e Compare April 27, 2018 18:13
@Foghrye4
Copy link
Contributor Author

Ok, now its finally working fine.
screenshot from 2018-04-27 21-05-36
(sampling only cubicchunks.server. package)

@Barteks2x
Copy link
Member

Barteks2x commented Apr 27, 2018

This seems to actually be either faster to sort or slower overall than the first implementation. Can you actually measure the time it takes to run the methods on average with the first and the current version? (git reflog should still show those old commits that you overwrote, unless you ran git gc)

while (next == null && i < start + size) {
T e = (T) data[i++];
if (toRemove.remove(e)) {
remove();
Copy link
Member

@Barteks2x Barteks2x Apr 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it actually is slower, this is likely the part responsible for it. Just do contains() check and skip it here and do mass-removal on tick. The System.arrayCopy during removal of every single element is what this PR was supposed to avoid.

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from cecb80e to 5bb6c7c Compare April 28, 2018 17:16
@Foghrye4
Copy link
Contributor Author

I made a one simple trick to use sorting algorithm in getting rid of dead elements.
Here is a results of JMH benchmarking:

Benchmark                Mode  Cnt      Score      Error  Units
MyBenchmark.testNew  thrpt   20  63730.222 ± 5343.953  ops/s
MyBenchmark.testOld  thrpt   20    771.625 ±  543.384  ops/s

"Old" - is a yesterday version of a list.
"New" - is current version of a list.
Here is a benchmark code: https://pastebin.com/7PnNZTNJ
Here is a VisualVM results:
Imgur

@Barteks2x
Copy link
Member

Barteks2x commented Apr 28, 2018

This just tests the lists, not the whole PlayerCubeMap.tick() which is what actually counts. A lot of time is spent in hasNext, which calls peekNext which removed any elements in the toRemove set, one by one. With System.arrayCopy each time. And it is an issue because sort() isn't done every tick, you should probably just skip the al;ready removed elements during iteration.

@Foghrye4 Foghrye4 force-pushed the remove_cubewatcher_fix branch from 5bb6c7c to c53097e Compare April 28, 2018 18:04
@Foghrye4
Copy link
Contributor Author

Done as requested.

@Barteks2x
Copy link
Member

And update the sampler/profiler screenshot

@Foghrye4
Copy link
Contributor Author

Sampling results:
screenshot from 2018-04-28 21-16-12

@Barteks2x
Copy link
Member

As you can see, hasNext takes significantly less time than before. Merging it now.

@Barteks2x Barteks2x merged commit 86129f7 into OpenCubicChunks:MC_1.12 Apr 28, 2018
@Foghrye4 Foghrye4 deleted the remove_cubewatcher_fix branch April 28, 2018 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants