Could you elaborate on that a little? I have never really heard of this class before and I’m really confused as on how to use it. I can’t seem to insert any of its inherited classes into the editor either.
Oddly enough, part recycling resulted in the CPU cost being practically the same, except with a wider range below and above of about a whole percentage. My minimized particle settings ranged from .7% to 2.5% instead of its previous 1.2% to 2%. The same happened to the regular example I provide in the module (default settings). It used to range from 4.7% to 6%, but now ranges from 4.3% to 7%.
If I make the cache completely full so it is impossible for it to go empty and force another part creation, the variation range drops, but the average CPU cost is actually about .5% higher than before. I’m not sure if I’m just going through too much work to recycle the particles, or if it actually isn’t helping, but this is definitely not helping aid the performance.
Currently I’m setting the particles in a folder inside the 3D Emitter “class” (which is literally just another folder), meaning they do store themselves in the Workspace in case the emitter is disabled. But I still want to cache particles if the emitter does happen to be only disabled temporarily.
Most likely, the setting of part properties and the actual rendering dominate the time to update. Even with little or no performance improvement, I would expect pooling and re-use to have some benefits in terms of memory usage, especially on lower-end machines and after the emitter has been going a while, but I’d need to look closely at how instances are allocated and cleaned up to say for sure.
Alright cool.
I’d like to know how much localizing functions actually do if ya wanna benchmark it. I can’t atm unfortunately.
It saved about .2% to .3% on the two individual particles I use, definitely a noticeable effect. Probably even more effect on large groups of particles. I imagine it helped even more on the lightning generation because that was really heavy on the math.random. Thanks so much for the help!
These classes can only be instantiated from code, like:
Instance.new(“SphereHandleAdorn”, game.Workspace)
Their “Adornee” property must be set to a part to be visible.
The CFrame property can be set and will not have the performance issues of setting part CFrames/Mesh offsets.
You can reasonably set several thousand handleadorn cframes every frame.
@Osyris
https://twitter.com/iamosyris/status/872226207705710592
Please keep in mind PSA: Don't use Instance.new() with parent argument when trying to improve performance
This is only especially relevant in regards to network traffic if you’re creating them on the server with the intention to replicate them (though you may still want to do this anyway for this usecase since you’re likely creating a bunch of particles).
From my experience, the savings from moving from PartInstance → Adorns (in relation to local performance) dwarf any savings from lua assignment order/micro-optimizations.
Woops negate me then… Thanks for the heads up!
I don’t think that’s true in general - even regardless of the network traffic there is a lot of overhead in parenting first for some cases, specifically for unexpected physics work (in fact physics-related slowdown is why I wrote that post in the first place).
One problem I’ve run into: When making this toggleable, the if statements I added to differentiate between HandleAdornment particles and regular particles actually left the HandleAdornments costing more CPU than the regular parts did (.5% increase). The CPU cost average for regular part particles was doubled to 11-12% instead of ~5-6%.
I don’t think this is a good thing. I would love to use handle adornments as a speedy, CPU-conserving alternative, but I don’t think that completely switching over to them would be a great idea because that kind of removes the entire purpose of this module in the first place. Thanks for the suggestion though!
EDIT: Wait a minute, just realized I had mesh tween off when dealing with the part-based particles. However, it was still an additional 1-2% for those and, again, the CPU on the HandleAdornments still was higher than before.
Note sure what you’re doing, but these particles sound like they shouldn’t be performance-blocked on cpu/lua speed. (I made the switch in a game I am working on recently and had major savings in the “updatePrepare” portion of the rendering update).
Can you show the microprofiler screen of what you see (that gave you the stats you mentioned), and a description/pseudocode of your code?
I’m just using the Script Performance tab in studio. All the code is still in the module, just blocked out. I can try and give you a general idea of what I’m doing but I’ve never really got into optimizing before so I’m not very good at telling what’s good. I’ll see what I can do though next time I’m using Windows.
I originally wrote this part of the module for @Patrickblox in hopes we could use it in Project Autumn and maybe our Star Wars game too, but he said anything above 3% of CPU usage was too expensive and shouldn’t be used…
The better way of measuring if performance increase is using tick() and measuring how long it took for a block of code to execute.
could y’all ping me when this module gets optimized enough that i can use it without having to worry about lag
thx
So I went through and did a quick average tick benchmark and these were the results.
Average tick was 0.00034065723419189 for default settings
Average tick was 0.00079620313644409 for default with Adornment feature in module but disabled
Average tick was 0.00079818820953369 for default with Adornment feature enabled
Average tick was 0.00018224954605103 for minimized settings
Average tick was 0.00024948358535767 for minimized with Adornment feature in module but disabled
Average tick was 0.00021843004226685 for minimized with Adornment feature enabled
For “Adornment feature in module but disabled” I mean that I had all the code in the module to allow for adornments, but disabled it for that particle. Each benchmark consisted of 500 loops in the system, averaging the time each tick took.
My code works out kind of like this:
- Set up emitter: Creates the emitter, sets everything up, preps settings and compiles arrays. This is done outside of the loop so it does not affect performance.
- Toggle emitter: This is a second-hand setup that basically decompiles everything and throws the emitter in the loop. This is also initial and should not affect performance, it actually kind of enhances it because it “unpacks” everything ahead of time.
- The Loop:
- Goes through emitters and emits particles accordingly. When a particle/adornment is created, it’ll pull it out of the cache and resets it if already exists, if not, it creates a new one by cloning the base particle or adornment.
After that, it randomizes the particle stats according to the settings you picked.
It’ll parent the particle to either the workspace or the base particle, depending on if you used parts or adornments. If adornments, it’ll be part of the base particle and will have to have its visibility settings reset.
Depending on if you had it use meshes or not, it’ll use tweenservice to scale them. Transparency also uses tweenservice. - Goes through each individual particle and steps it’s properties (position with acceleration and gravity into account). If mesh tweening was not used or it is a handle adornment, it manually has to set size here. Problem is if it’s a handle adornment, it has 3 different if statements it has to go through due to different types existing with different properties for size.
CFrame is set here lastly, which is literally a single line of code using the pre-calculated position with an additional CFrame.Angles() for rotations. - If the particle happens to despawn, it’ll either store it in the cache and reset it’s visibility settings (there are more if it’s an adornment), or it’ll just destroy the particle if the cache is full or not enabled.
For this test I did enable caching and the cache slowly balances itself after some time, but it the average ticks would still stay in around the same proportion otherwise.
Overall, I’d say the adornments would help performance, but if I wanted the full benefit of them, I would have to switch the entire module to using just adornments, not regular parts, and not duel-wielding adornments and parts (which is what I am doing currently). Even if the performance would be significantly improved, it kind of removes the goal I was aiming for, which is using meshparts, material, and lighting on physically 3D particles.
FINAL EDIT (I accidentally posted before I was done writing this, woops): I did leave the adornment code in the module though if you want to take a look. However, I didn’t think people were actually going to go through my code and help me optimize it, so I haven’t really put any comments in it. Sorry about that.
the goal with switching to handleadorns isn’t to save cpu time (but you will if you change from setting part size to adorn height/radius/etc), it saves updateprepare time in the rendering thread
I think this is about as optimized as it’s gonna get unless anyone else has any more suggestions. Thanks for all your help guys! There is an example particle I put in the module if you guys want to get a quick demo on how to use it.
Hello @Ashtheking300, you wanted me to notify you when this was done. I don’t know how good it is though, lol.
thx fam
I tried it out and it was really cool, I recommend trying it despite what your CPU can handle… maybe try lowering your graphics?