PSA: Don't use Instance.new() with parent argument

Here is a PR which does this:

2 Likes

I used option two religiously and started trying and learning option 3 because I find option 1 to be the unwanted method of doing things.

I’m a little frustrated with not being sure what to do about this now, but it could explain some recent lag since a gun framework we use in my clan has the create method. I’m just hoping there’s going to be a optimization at some point for whatever is going on because I’m still in the process of learning new things roblox added on my very long break.

It seems to be okay with StringValue or IntValue though. That’s mostly what I do when I’m instancing objects with the parent argument.

11 Likes

This makes sense, there aren’t nearly as many listeners needed for Value classes.
The part class has a ridiculous amount of members.

7 Likes

What if I am creating new instances within created instances, even after I set its properties? For example, if I am creating a GUI like this:

local Gui = Instance.new("ScreenGui") local Frame = Instance.new("Frame") -- Give the Frame properties Frame.Parent = Gui local TextLabel = Instance.new("TextLabel") -- Give the TextLabel properties TextLabel.Parent = Frame

Would it be better for me to set the parent of “Gui” to CoreGui after I create all of its children, or would it be better to set its parent first,before I create all of its children and parent its children to it (i.e. ‘Gui = Instance.new(“ScreenGui”, game.CoreGui)’ would replace the first line, as opposed to sticking ‘Gui.Parent = game.CoreGui’ at the end)?

4 Likes

Calling Instance.new with the second argument isn’t the root problem here.

The root problem is that setting Size has a side effect where it runs an extremely expensive collision check to make sure that it doesn’t intersect with nearby parts.

The overhead from resizing becomes worse is in the vicinity of other parts, presumably because the narrowphase of the collision check is really slow. My workstation (i7-4790K, 1070) was struggling to push 5 FPS when I had to resize big parts for the devforum offices.

Is this a good enough excuse to get rid of that behavior?

31 Likes

I always thought this was obvious. Once any instance enters the DataModel, it becomes “tainted”, in that anything might happen to it at any time afterwards, even after it gets removed from the DataModel.

This is why it’s impossible to program gear correctly. This is why using the index operator to get the children of an instance is bad practice. Scripts have no means to acquire ownership of an instance, even temporarily.

So, the solution, when creating an instance from scratch, is to make sure the script has initialized it to complete satisfaction before allowing it to enter the game. And to stop trusting any bit of information it contains from there afterward. Instances should never be used to contain important information; that’s what variables and tables are for. An Instance itself is merely a frontend for whatever effect you want it to have in the game.

17 Likes

Parts are created at the origin, so your assignment of CFrame isn’t actually doing anything. Try changing the CFrame to something like 100,100,100 and you’ll probably get a significant speed loss.

aiight

17 Likes

We can use string patterns in tye find widget?? Awesome!

I’ve rewritten Instance.new a bit using that “wait until thread yields” idea.
It’s almost as fast as setting .Parent as last field, much faster than the regular Instance.new(class,parent).
(I said “until thread yields”, but it’s more like “after all pending threads yielded”, can’t do better)

Here’s the (test) code, along with the output of running it 5x: Instance.new() with parent argument - Pastebin.com
Here’s a test run:

0.00053739547729492 -- Instance.new(a) setFields() setParent()
0.075802326202393 -- Instance.new(a,b) setFields()
0.00056886672973633 -- custom_new(a,b) setFields()
nil -- parent of custom_new(a,b) right after setting the fields
Folder 0.0021722316741943 -- parent + passed time after obj.Changed:wait()

Basically, add the end of the tick, it (only then) sets the parent of all custom_new(a,b) objects.
The “mutex function” only runs once, at the end of the tick that mutex:Fire() got called.
So after wait()or coroutine.yield(), the parent is guaranteed to be set to what you wanted.
Seems like it’s almost as fast as setting the Parent afterwards, but Parent is only set after a semi-tick.
You can also do mutex.Event:wait() if you need to wait until everything (of the current tick) is parented.

TL;DR: “Fixed” Instance.new(a,b), but Parent is only set somewhere before the next tick
(it’s still better to just manually replace all Instance.new(a,b) with a variant that sets .Parent afterwards)

One thing I noticed:
Instance.new("Part",workspace) takes longer depending on how many parts are already present.

2 Likes

Does the advice to not use the parent parameter apply to cases where the parent is not a descendant of the DataModel?

E.g., is this okay?

local p = Instance.new("Part")
local f = Instance.new("Fire", p)
f.Heat = ...

Is the DataModel itself where the performance problems come in? Or do descendants in general cost here?

1 Like

As long as it isn’t a descendant of game, it’s ok.
If your part p would be a descendant of game, it wouldn’t, but that isn’t the case here I think.
So you can create one object, keep adding objects to it and not care about this, then set the top object’s Parent.

1 Like

This is very useful information. Thanks for sharing with us!

2 Likes

The difference between parent/property assignment order for this code exists but is pretty small and unlikely to be measurable unless your hierarchy is much deeper. The main issue is with parenting to game.

4 Likes

So the problem isn’t necessarily with the parent argument, it’s just that changing certain properties of things parented to game can cause performance issues?

1 Like

This explains some things…

I’ve always avoided this because I didn’t want objects being sent over the network until they were ready.

This is usually because I want to set the name before putting it in the game. (ChildAdded events). Glad to know that’s paying off!

10 Likes

All I know is that setting .Size of a part is one of the most expensive property changes I’ve ever seen in my life, and it’s because of these collision checks. Changing a part’s size on Heartbeat is not a good idea right now.

I cannot wait til we are able to remove collisions off of a part.

3 Likes

It’s not quite precise. Changing properties of things parented to game requires additional work (sometimes, a bit; sometimes, a lot). Setting parent first pretty much guarantees that you’re doing a bunch of these redundantly, that can result in performance that ranges from slightly worse than usual to catastrophically worse than usual.

1 Like