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
⌛
This post has been reserved for your question.
Hey @Verse! Please useTIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here./close
or theClose 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.
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
💤
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.
it does
Thats why im concerned
Can you show the results of profiling?
(Also note: Class initialization is only on first use)
Yea its startup and when stuff is loaded the first time. Its about 500ms in total by classloading and unessesary <cinit> (static code init)
then you should maybe check
-
static
blocks
- which classes cause the issue
and actually show these results hereI did so
Why do i need to share the class source, because its a general sturcture problem. It looks messy
I am not asking for the source code but for profiling results
Oh ok
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 thatI do have static code of Reflection init that shows up, but that seems to have little impact mostly
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)
Yea i see all of that
From Startup with all features disabled:




And this is after first run (joining world the first time etc. ) with all features disabled:


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
Here is some of the code
that part means most of the class initialiasation is spent on the reflection stuff

Also can you show the profiling results relevant to running transformers?



Can you not expand these parts?
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
Did you do profiling or sampling?
Also what exactly is preInit?
This is when all features are enabled (startup) i left out the lower values as it would be too much






sampling, profiling gives me very strange first run results and usually crash or lag
where I register forge events and things like that
if you look at these time estimates, you see that the load() method doesn't actually seem to do that much work

Note that sampling is a very rough estimate and can be very inaccurate
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)
in that screenshot, can you expand the clinit things?

yes sure
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
well this

yes, profiling has a high overhead
the issue with
loadClass()
is that it isn't necessarily class loading but also other thingsalso 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
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?
yes
at least it does look like that
ok
i will try a profiler lets see if i can get it work for startup but i dont have much hope
When you did profiling, I assume you only specified the package of your application/mod?
Can you show the profiling settings maybe?
depends. if i try to profile everything i can say "goodbye visualvm" xD. What should i specify?
yes
profile only the classes of your mod

In "Profile Classes", you'd need to specify what you want to profile
for example you can enter
metweaks.**
thereLike that?

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?
im not sure what that means sorry, can you elaborate
How do you attach VisualVM to your application?
I select GradleStart (ForgeGradle has a RunClient command to start mc in dev environment)

so you first run Minecraft and then you attach VisualVM?
yes
i make the thread sleep for 10s to give me time
ah ok
I see
in the coremod startup
don't forget to remove it when you're done ;)
yeah, try profiling with that
and see what happens
okay profiling cause Minecraft to get stuck on Object.wait()
how can i fix this
How do you see that?

when i start the sampler while the profiler doesnt do anything anymore
oh within visualvm
that's only 58ms
it just told me that the profiler isnt responding the game is still in early startup let me try again
Did Minecraft start?
with your mod
no its stuck
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?Wait i tried another time
So i have no cpu consumation now
But nothing moves
Mc is in early init
let it run for a minute or whatever, end profiling and check the results
yes, profiling is slow
I have done so now
it hasnt moved from that for a minute

Can you show the results? The whole VisualVM window?
yes

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 stuffThats what the sampler shows me about the profilers state rn




the profiling threads are probably not that important
Yea but i want to find out whats getting it stuck and how to wake it up
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
In what way can it give me info?
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
I feel like visualvm doesnt like the ASM stuff
that's possible
you could try other tools like JFR if you want to though idk how good it is for performance
yea its that its stuck at the first asm patch :(
wow
wait i got an idea i could just blacklist the coremod
right?
not sure if that ruins the results tho
you can, yes
it means you wouldn't be getting results from coremod ofc
btw https://docs.oracle.com/javase/10/troubleshoot/troubleshoot-performance-issues-using-jfr.htm#JSTGD307 if you want to use JFR/JMC
Troubleshoot Performance Issues Using JFR
This chapter identifies performance issues with a Java application and debugs these issues using the Java Flight Recorder.
btw why are all these others blacklisted?

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 costbruh 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
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
Ah
So how do i properly share profile results
Is there some sort of export thing?
I have VisualVM if necessary
File > Save As
Doesnt seem to work one sec
It seems like the registerBlock calls are like 1/5-2/5 of startup time

more like 2/5 - there's also the makeDistance method
im registering 176 blocks
and then there's the thing with findMethod inside clinit - but the issue there is searching for the method with ASM

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
these are the features itself tho, but why are there asm code everywhere when you expand these
because you are calling things like
findMethod()
which uses asmbtw you can also reverse calls

thats new to me tbh
that shows you what is called that costs CPU time

and analyze where these methods are called from
this is it

then try optimizing that
Do you really need it?

yea i need to call that but why does it only show up in the profiler and not sampler
and there's also some iteration that happens when computing slab distances that is costing you performance

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
Well but where does this call asm? (which is shows in the profiler)
Can you try replacing the
findMehtod
with your own logic?
LOTREntityNPC.class.getDeclaredMethod("getPoisonedArrowChance")
or similar
btw are you repeatedly calling that method?no, where do you see that?
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
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
method handles would be an alternative to reflection
i dont see how objectweb asm would get there else
What classtransformer are you using here?
Do you know why that needs to be processed?
maybe you can work on that
yea im modifying that class in asm
Is that strictly necessary?
It does seem a bit like the ASM stuff causes the issues
LOTRENtityNPC is a part of the lotr mod and im patching/changing behavior there
that sort of patching can be pretty expensive
im not directly touching LOTREntityNPC but maybe it loads something that gets patched
In what way are you modifying behavior there?
you could in principle try that - maybe it would be bypassing something
But i was mostly looking to isolated features probably than the modules in itself (a different thing todo)
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
well have you seen the code that i sent, like the sourcecode?
yes
you mean that, right?
this
No I didn't look at it
Can you?
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?
Not in this src
yes
Is your ASM code only affecting/processing the lotr mod?
its not processing my mod. But when i load a lotr class there is gonna affect my load time too
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
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?
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
Thats a copyright violation
idk the license of the mod
ARR, closed source
in principle, you could try to load the stuff in another thread
What do you think about my code design/structure/readability/isolation of modules
I don't know your mod but it looks fine
I haven't really looked at the code itself though
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
is there stuff that's needed in multiple of your classes?
after splitting
yea for example look at ClientEvents and GuardEvents and RenderHealthBar
well it doesn't seem like you are duplicating stuff there
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
the flamegraph is really fast in knowing what take time
and you can see if buffer are copied for nothing or called multiple time when they should not.
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
Well his goal is to have better performance, without benchmark you can decide what is best.
they explicitly mentioned startup performance
without benchmark, anything you try can just be for nothing.
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
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
that effect is insignificant
unless maybe you have millions of classes
It should be but is it then
I have broken the big classes into smaller ones
Well, you do a lot of Minecraft-specific stuff
so you call
register(this)
somewhere I thinkAnd i sorted the registration of handlers to a more efficient manner (less registrations)
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
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
You are doing findMethod calls
Are you maybe looking up the same method in multiple locations?
or field or whatever
No not that i know of
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 ;)Thank you so much for taking your time
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💤
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.