Performance & Readability Improvements

I do have quite big classes, I wanted to break them down into smaller classes but i entered issues while doing that. My goal is better readability, equal performance compared to now when all modules enabled and better performance than now when all modules disabled. Im trying to avoid class loading,initialisation and registration of handlers that arent needed aswell as them registrated multiple times. For example if i have a field that controls whether a module is going to be loaded or not, but the field also needed for a module and changing during runtime, then i have basically one part of the module i the class that refers execution to modules and one part in the module class, which doesnt look good. Also if i have smaller classes i need more total time to load these and they consume more file size and metaspace memory? I also noticed if i load on demand that i will get minor lag spikes. If i though keep them in big classes then i will have the best performance when everything is used and loaded but bad performance when only a fraction is enabled.
173 Replies
JavaBot
JavaBot4w ago
This post has been reserved for your question.
Hey @Verse! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
dan1st
dan1st4w ago
Don't worry about class initialization unless a profiler tells you it's a problem it's probably not a noticeable difference if you have performance issues, you should use profilers or similar tools to find out why it's probably not the number/size of classes but more likely things like indirections, handling some stuff less efficiently or GC stuff
JavaBot
JavaBot4w ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
Verse
VerseOP4w ago
it does Thats why im concerned
dan1st
dan1st4w ago
Can you show the results of profiling? (Also note: Class initialization is only on first use)
Verse
VerseOP4w ago
Yea its startup and when stuff is loaded the first time. Its about 500ms in total by classloading and unessesary <cinit> (static code init)
dan1st
dan1st4w ago
then you should maybe check - static blocks - which classes cause the issue and actually show these results here
Verse
VerseOP4w ago
I did so Why do i need to share the class source, because its a general sturcture problem. It looks messy
dan1st
dan1st4w ago
I am not asking for the source code but for profiling results
Verse
VerseOP4w ago
Oh ok
dan1st
dan1st4w ago
and maybe it isn't even the classes themselves but stuff you do within these classes e.g. private static final SomeClass someField = new SomeClass() where the SomeClass constructor does expensive operations and maybe you now have multiple classes repeating that
Verse
VerseOP4w ago
I do have static code of Reflection init that shows up, but that seems to have little impact mostly
dan1st
dan1st4w ago
a profiler should tell you what the actual issue is it might not be what you expect for example having some constructor call from some library somewhere in two different classes instead of once in the big class (just as an example but profilers are what tells you what the issue is)
Verse
VerseOP4w ago
Yea i see all of that
Verse
VerseOP4w ago
From Startup with all features disabled:
No description
No description
No description
Verse
VerseOP4w ago
No description
Verse
VerseOP4w ago
And this is after first run (joining world the first time etc. ) with all features disabled:
No description
No description
Verse
VerseOP4w ago
As I said if i enable all features i will get obvious file and metaspace overhead and minor lags when things are done the first time
Verse
VerseOP4w ago
Here is some of the code
dan1st
dan1st4w ago
that part means most of the class initialiasation is spent on the reflection stuff
No description
dan1st
dan1st4w ago
Also can you show the profiling results relevant to running transformers?
Verse
VerseOP4w ago
No description
No description
No description
dan1st
dan1st4w ago
Can you not expand these parts?
Verse
VerseOP4w ago
i have mostly broken down the classes here which reduce the time from 500 to about 100ms in preinit, through avoiding classload but it doesnt look very fancy imo. And the results when all features are enabled make about 800ms together i think let me get them no i cannot in terms of it doesnt let me
dan1st
dan1st4w ago
Did you do profiling or sampling? Also what exactly is preInit?
Verse
VerseOP4w ago
This is when all features are enabled (startup) i left out the lower values as it would be too much
No description
No description
No description
No description
No description
No description
Verse
VerseOP4w ago
sampling, profiling gives me very strange first run results and usually crash or lag where I register forge events and things like that
dan1st
dan1st4w ago
if you look at these time estimates, you see that the load() method doesn't actually seem to do that much work
No description
dan1st
dan1st4w ago
Note that sampling is a very rough estimate and can be very inaccurate
Verse
VerseOP4w ago
i know that but when the profiler gives me results that are astronomically its realy strange. I have been profilng mc many times and its basically the first few minutes it doesnt respond and after that its like 1 fps and the values are useless for init as they show everything realy high a lot of the lag it seems to cause by itself. Sampler helped me the most so far. But im sampling at a 1ms rate so it shouldnt be too bad (100ms was standard)
dan1st
dan1st4w ago
in that screenshot, can you expand the clinit things?
No description
Verse
VerseOP4w ago
yes sure
dan1st
dan1st4w ago
I think that actual class loading isn't the problem - but what you are doing while the classs are initializing but even that doesn't seem to be most of the issue
Verse
VerseOP4w ago
well this
No description
dan1st
dan1st4w ago
yes, profiling has a high overhead the issue with loadClass() is that it isn't necessarily class loading but also other things
Verse
VerseOP4w ago
also if i dont turn my settings to zero i had no chance to get it run at all so it will likely crash when i profile startup. i know it can be transformers or cinit
dan1st
dan1st4w ago
it includes class initialization but also waiting for other classes to be initialized etc (it isn't possible to initialize multiple classes concurrently) You are using VisualVM, right?
Verse
VerseOP4w ago
yes
dan1st
dan1st4w ago
at least it does look like that ok
Verse
VerseOP4w ago
i will try a profiler lets see if i can get it work for startup but i dont have much hope
dan1st
dan1st4w ago
When you did profiling, I assume you only specified the package of your application/mod? Can you show the profiling settings maybe?
Verse
VerseOP4w ago
depends. if i try to profile everything i can say "goodbye visualvm" xD. What should i specify?
dan1st
dan1st4w ago
Verse
VerseOP4w ago
yes
dan1st
dan1st4w ago
profile only the classes of your mod
Verse
VerseOP4w ago
No description
dan1st
dan1st4w ago
In "Profile Classes", you'd need to specify what you want to profile for example you can enter metweaks.** there
Verse
VerseOP4w ago
Like that?
No description
dan1st
dan1st4w ago
yes I thought about that menu but yeah also make sure to profile the application when starting it And are you actually profiling the application or the whole Gradle build?
Verse
VerseOP4w ago
im not sure what that means sorry, can you elaborate
dan1st
dan1st4w ago
How do you attach VisualVM to your application?
Verse
VerseOP4w ago
I select GradleStart (ForgeGradle has a RunClient command to start mc in dev environment)
No description
dan1st
dan1st4w ago
so you first run Minecraft and then you attach VisualVM?
Verse
VerseOP4w ago
yes i make the thread sleep for 10s to give me time
dan1st
dan1st4w ago
ah ok I see
Verse
VerseOP4w ago
in the coremod startup
dan1st
dan1st4w ago
don't forget to remove it when you're done ;) yeah, try profiling with that and see what happens
Verse
VerseOP4w ago
okay profiling cause Minecraft to get stuck on Object.wait() how can i fix this
dan1st
dan1st4w ago
How do you see that?
Verse
VerseOP4w ago
No description
Verse
VerseOP4w ago
when i start the sampler while the profiler doesnt do anything anymore
dan1st
dan1st4w ago
oh within visualvm that's only 58ms
Verse
VerseOP4w ago
it just told me that the profiler isnt responding the game is still in early startup let me try again
dan1st
dan1st4w ago
Did Minecraft start? with your mod
Verse
VerseOP4w ago
no its stuck
dan1st
dan1st4w ago
Can you click on the button to end profiling? I guess the most likely thing is that profiling is just much slower you should probably add org.graalvm.** to the "Exclude outgoing calls" thing Once you did that, can you show the full results?
Verse
VerseOP4w ago
Wait i tried another time So i have no cpu consumation now But nothing moves Mc is in early init
dan1st
dan1st4w ago
let it run for a minute or whatever, end profiling and check the results yes, profiling is slow
Verse
VerseOP4w ago
I have done so now
Verse
VerseOP4w ago
it hasnt moved from that for a minute
No description
dan1st
dan1st4w ago
Can you show the results? The whole VisualVM window? yes
Verse
VerseOP4w ago
No description
dan1st
dan1st4w ago
you could in principle also try other profilers if VisualVM doesn't work well I mean, these are results It does seem like ASMConfig.read() does work And also the other ASM stuff
Verse
VerseOP4w ago
Thats what the sampler shows me about the profilers state rn
No description
No description
No description
No description
dan1st
dan1st4w ago
the profiling threads are probably not that important
Verse
VerseOP4w ago
Yea but i want to find out whats getting it stuck and how to wake it up
dan1st
dan1st4w ago
you can also try making a thread dump for that instead of sampling a single thread dump once it's stuck - without doing sampling maybe two
Verse
VerseOP4w ago
In what way can it give me info?
dan1st
dan1st4w ago
a thread dump tells you what the application is currently doing while sampling tries to find an average/aggregate data If you want to do a thread dump and VisualVM doesn't do it, you can use jcmd <process ID> Thread.print
Verse
VerseOP4w ago
I feel like visualvm doesnt like the ASM stuff
dan1st
dan1st4w ago
that's possible you could try other tools like JFR if you want to though idk how good it is for performance
Verse
VerseOP4w ago
yea its that its stuck at the first asm patch :(
dan1st
dan1st4w ago
wow
Verse
VerseOP4w ago
wait i got an idea i could just blacklist the coremod right? not sure if that ruins the results tho
dan1st
dan1st4w ago
you can, yes it means you wouldn't be getting results from coremod ofc
dan1st
dan1st4w ago
Troubleshoot Performance Issues Using JFR
This chapter identifies performance issues with a Java application and debugs these issues using the Java Flight Recorder.
Verse
VerseOP4w ago
btw why are all these others blacklisted?
No description
dan1st
dan1st4w ago
if you call a method in String, you don't want to see what happens inside these String operations you are typically not interested in things happening inside JDK stuff etc so you don't want to pay the performance cost
Verse
VerseOP4w ago
bruh i cant blacklist classes that i did put in the profile classes list so i have to add every single package manually :D but it works now
dan1st
dan1st4w ago
that thing isn't really a blacklist it's if your profiled code is calling something else, that thing decides whether it's profiled as well
Verse
VerseOP4w ago
Ah So how do i properly share profile results
dan1st
dan1st4w ago
Is there some sort of export thing? I have VisualVM if necessary File > Save As
Verse
VerseOP4w ago
Doesnt seem to work one sec
dan1st
dan1st4w ago
It seems like the registerBlock calls are like 1/5-2/5 of startup time
No description
dan1st
dan1st4w ago
more like 2/5 - there's also the makeDistance method
Verse
VerseOP4w ago
im registering 176 blocks
dan1st
dan1st4w ago
and then there's the thing with findMethod inside clinit - but the issue there is searching for the method with ASM
No description
dan1st
dan1st4w ago
and that seems to cause some of the startup performance issues out of the things you profiled, these seem to be the main causes of problems
Verse
VerseOP4w ago
these are the features itself tho, but why are there asm code everywhere when you expand these
dan1st
dan1st4w ago
because you are calling things like findMethod() which uses asm
dan1st
dan1st4w ago
btw you can also reverse calls
No description
Verse
VerseOP4w ago
thats new to me tbh
dan1st
dan1st4w ago
that shows you what is called that costs CPU time
No description
dan1st
dan1st4w ago
and analyze where these methods are called from
Verse
VerseOP4w ago
this is it
No description
dan1st
dan1st4w ago
then try optimizing that Do you really need it?
Verse
VerseOP4w ago
No description
Verse
VerseOP4w ago
yea i need to call that but why does it only show up in the profiler and not sampler
dan1st
dan1st4w ago
and there's also some iteration that happens when computing slab distances that is costing you performance
No description
dan1st
dan1st4w ago
samplers can be inaccurate I am not completely sure this is the issue but it's definitely something to investigate Can you maybe do it differently? btw this is a reverse view
Verse
VerseOP4w ago
Well but where does this call asm? (which is shows in the profiler)
dan1st
dan1st4w ago
Can you try replacing the findMehtod with your own logic? LOTREntityNPC.class.getDeclaredMethod("getPoisonedArrowChance") or similar btw are you repeatedly calling that method?
Verse
VerseOP4w ago
no, where do you see that?
dan1st
dan1st4w ago
I didn't see that - I wanted to check because if you would be calling it multiple times, there are alternatives like method handles but it looks like there may be agents involved here
Verse
VerseOP4w ago
well im saving the Method object and calling the method through reflection when i need it Do you mean that I think that it causes LOTREntityNPC to be processed in a classtransformer
dan1st
dan1st4w ago
method handles would be an alternative to reflection
Verse
VerseOP4w ago
i dont see how objectweb asm would get there else
dan1st
dan1st4w ago
What classtransformer are you using here? Do you know why that needs to be processed? maybe you can work on that
Verse
VerseOP4w ago
yea im modifying that class in asm
dan1st
dan1st4w ago
Is that strictly necessary? It does seem a bit like the ASM stuff causes the issues
Verse
VerseOP4w ago
LOTRENtityNPC is a part of the lotr mod and im patching/changing behavior there
dan1st
dan1st4w ago
that sort of patching can be pretty expensive
Verse
VerseOP4w ago
im not directly touching LOTREntityNPC but maybe it loads something that gets patched
dan1st
dan1st4w ago
In what way are you modifying behavior there? you could in principle try that - maybe it would be bypassing something
Verse
VerseOP4w ago
But i was mostly looking to isolated features probably than the modules in itself (a different thing todo)
dan1st
dan1st4w ago
you could do lazy loading for these things or you could ensure that the classes of other modules you don't need aren't loaded when they aren't used
Verse
VerseOP4w ago
well have you seen the code that i sent, like the sourcecode?
dan1st
dan1st4w ago
yes you mean that, right?
Verse
VerseOP4w ago
this
dan1st
dan1st4w ago
No I didn't look at it
Verse
VerseOP4w ago
Can you?
dan1st
dan1st4w ago
I assume the things you are accessing with reflection are private? Where are you doing the ASM stuff? or is that not you at all?
Verse
VerseOP4w ago
Not in this src
Verse
VerseOP4w ago
Verse
VerseOP4w ago
yes
dan1st
dan1st4w ago
Is your ASM code only affecting/processing the lotr mod?
Verse
VerseOP4w ago
its not processing my mod. But when i load a lotr class there is gonna affect my load time too
dan1st
dan1st4w ago
Well I think you'd have to do that in all cases so you can maybe optimize the processing part but probably not remove it If you don't need something, you could try to avoid loading it - but idk whether it improves anything
Verse
VerseOP4w ago
yea there is stuff to optimize but how about the class loading itself, i mean the code structure/ lazyloadi use for things. How do i prevent lazyload freezing the game when im playing?
dan1st
dan1st4w ago
and maybe you could even get rid of the runtime transformation by just modifying the mod JAR directly - and letting users install the modified mod if that is applicable
Verse
VerseOP4w ago
Thats a copyright violation
dan1st
dan1st4w ago
idk the license of the mod
Verse
VerseOP4w ago
ARR, closed source
dan1st
dan1st4w ago
in principle, you could try to load the stuff in another thread
Verse
VerseOP4w ago
What do you think about my code design/structure/readability/isolation of modules
dan1st
dan1st4w ago
I don't know your mod but it looks fine I haven't really looked at the code itself though
Verse
VerseOP4w ago
Its the one in my bio, i just think i made it worse by splitting classes if everything is enabled but better when nothing is enabled
dan1st
dan1st4w ago
is there stuff that's needed in multiple of your classes? after splitting
Verse
VerseOP4w ago
yea for example look at ClientEvents and GuardEvents and RenderHealthBar
dan1st
dan1st4w ago
well it doesn't seem like you are duplicating stuff there
sooluckyseven
sooluckyseven4w ago
GitHub
GitHub - async-profiler/async-profiler: Sampling CPU and HEAP profi...
Sampling CPU and HEAP profiler for Java featuring AsyncGetCallTrace + perf_events - async-profiler/async-profiler
GitHub
https://openjdk.org/projects/code-tools/jmh. Contribute to openjdk/jmh development by creating an account on GitHub.
sooluckyseven
sooluckyseven4w ago
the flamegraph is really fast in knowing what take time
sooluckyseven
sooluckyseven4w ago
and you can see if buffer are copied for nothing or called multiple time when they should not.
dan1st
dan1st4w ago
JMH isn't really useful for that because this isn't about benchmarks but more improving startup time/class loading/initialization microbenchmarks aren't really helping there
sooluckyseven
sooluckyseven4w ago
Well his goal is to have better performance, without benchmark you can decide what is best.
dan1st
dan1st4w ago
they explicitly mentioned startup performance
sooluckyseven
sooluckyseven4w ago
without benchmark, anything you try can just be for nothing.
dan1st
dan1st4w ago
I know how that works and good luck measuring classloading issues within a Minecraft mod with JMH And if you have a thing that's taking like a second to do one run and you want to optimize one-shot performance, JMH isn't needed async-profiler can be useful and I also thought about suggesting that as well but using JMH on something that doesn't work with JMH doesn't help and you don't have to link me a JMH/"measure, don't guess" talk
Verse
VerseOP4w ago
The issue i see is that you would have to profile the startup hundrets of times carefulöy to get an idea But i can see my old messy code being 10% faster in all attempts of startup with all features enabled 😭 Im a bit confused Are a bit smaller classes so much slower than big classes
dan1st
dan1st4w ago
that effect is insignificant unless maybe you have millions of classes
Verse
VerseOP4w ago
It should be but is it then I have broken the big classes into smaller ones
dan1st
dan1st4w ago
Well, you do a lot of Minecraft-specific stuff so you call register(this) somewhere I think
Verse
VerseOP4w ago
And i sorted the registration of handlers to a more efficient manner (less registrations)
dan1st
dan1st4w ago
so that means you let Minecraft do whatever it wants to do In principle, you could use something like async-profiler and profile both versions looking for differences but idk how much work you want to put into it
Verse
VerseOP4w ago
Well if i register all in one class i have the issue of handling events that i dont need that again costs runtime performance I did so rn
dan1st
dan1st4w ago
You are doing findMethod calls Are you maybe looking up the same method in multiple locations? or field or whatever
Verse
VerseOP4w ago
No not that i know of
dan1st
dan1st4w ago
What is your CustomRanged class doing? it seems like the initialization of that class is around 1/5 of the startup time of the profiling result you shared before so you might want to closely investigate that class Or you have VerticalSlab.makeInstance which was called 54 times, for example from BlocksCore#setupBlocks - maybe you could investigate that as well Also do you have any foreach loops in registerBlock that are doing like 2k iterations whereas registerBlock is called 176 times? @Verse ^ hints for investigating according to the profiling results ;)
Verse
VerseOP4w ago
Thank you so much for taking your time
dan1st
dan1st4w ago
If you want me to help with these specific things, feel free to ask oh and for CustomRanged, it might be ASM things for example if you show the VerticalSlab code, I might be able to find something you could try
JavaBot
JavaBot4w ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.

Did you find this page helpful?