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

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

So wouldn’t that mean that just setting the parent directly after creating the object has the same (negative) effect? I think that based on the title, a lot of people are confused and think that calling Instance.new(“Name”, parent) causes performance issues by itself.

Yep, same problem.
If you read his whole post, it’s pretty clear, but I can imagine a lot of people not reading it completely.
Maybe a TL;DR Only set the Parent property at the very end” would indeed be a good idea.

@zeuxcg
(added this mention by editing the post, not sure this even pings the person)

1 Like

If we’re about to make a lot of changes to an object, would it be a good idea to set its parent to nil, make all the changes, and then re-parent it?

1 Like

The answer is apparently yes.

9 Likes

Spam reparenting objects causes the explorer to consume a ton of memory, so be careful.

There’s an explorer running silently in the actual game? What the Hell.

1 Like