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

I’ve discovered a pretty bad performance issue in one of top games that has to do with Instance.new and wanted to write about this, since this is not obvious unless you know the system inside out.

Tthere are several ways to create a ROBLOX object in Lua:

  1. local obj = Instance.new(‘type’); fill obj fields
  2. local obj = Instance.new(‘type’, parent); fill obj fields
  3. local obj = util.Create(‘type’, { field1 = value1, … })

If you care at all about performance, please only use the first option - I will explain why.

In ROBLOX, objects start as detached from the game object and are left in this state up until you parent them to something that’s already a descendant of game. When the object is detached (meaning, .Parent = nil or maybe object is parented to some other object that is not parented to anything), changing the state of the object is very cheap - you’re just changing bytes inside some memory block.

Once an object is attached to game, a lot of internal ROBLOX systems start listening to property changes on the object and updating various internal data structures for the change to take effect. These updates can involve queueing changes for replication, updating physics contact state, queueing rendering state changes etc.

It’s thus important that when you create the object, the initial object field assignment is correctly classified as “initial setup” as opposed to “changing state of the existing object” - and the differentiator for this is .Parent, or rather the object being a descendant of game. For this reason, .Parent assignment should go after all property assignments during the object creation. You most likely want to connect all signals after that to make sure they are not dispatched during creation - so the optimal sequence is:

A. Instance.new
B. Assign properties
C. Assign Parent
D. Connect signals

Now let’s go over the options.

Option 1 allows you to be explicit about the order. You should make sure .Parent assignment is the last field that you assign - and in this case all property changes before that are treated as fast updates. You should use this. Example:

local wall = Instance.new("Part")
wall.Size = Vector3.new(10, 10, 1)
wall.CFrame = CFrame.new(x, y, z)
wall.Parent = workspace.Effects

This makes sure Size/CFrame updates are super fast, and when the object is inserted into the game, it’s in its final state - so we do the minimal amount of work necessary to initialize it. Here’s what actually happens:

  1. You create a Part with the CFrame of 0,0,0 and a default Size
  2. You assign Size and CFrame fields and then parent the part to workspace
  3. ROBLOX sets up property change listeners for replication, rendering, etc.
  4. ROBLOX queues a replication of a new instance with Size 10,10,1 and CFrame x,y,z
  5. ROBLOX updates physics contacts between this part and whatever is at the position x,y,z given the size 10,10,1

Option 2 explicitly assigns Parent as the first thing, which is the worst pattern you can use. Consider the version of the code above with Instance.new called with parent argument:

local wall = Instance.new("Part", workspace.Effects)
wall.Size = Vector3.new(10, 10, 1)
wall.CFrame = CFrame.new(x, y, z)

You may think that this is faster because you saved one assignment, but it’s actually much slower in this case. Here’s what happens:

  1. You create a Part with the CFrame of 0,0,0 and a default Size, and parent it to workspace.
  2. ROBLOX sets up property change listeners for replication, rendering, etc.
  3. ROBLOX queues a replication of a new instance with default values for the Size/CFrame
  4. ROBLOX updates physics contacts between this part and whatever is at the origin
  5. You set the part Size to 10,10,1
  6. ROBLOX queues a replication change for the Size
  7. ROBLOX updates physics contacts between this part and whatever is at the origin and overlaps with the part given the new dimensions
  8. You set the part CFrame to x,y,z
  9. ROBLOX queues a replication change for the CFrame
  10. ROBLOX updates physics contacts between this part and whatever is at the position x,y,z given the size 10,10,1

Notice how much more work we’re doing in this case! We are updating physics contacts completely redundantly multiple times - some of these updates may be very expensive - and also queueing useless replication changes which results in inefficient CPU/bandwidth usage.

Option 3 is arguably the worst:

local Create = LoadLibrary("RbxUtility").Create

local wall = Create "Part" {
Size = Vector3.new(10, 10, 1);
CFrame = CFrame.new(x, y, z);
Parent = workspace.Effects
}

It surely looks nice. However:

  1. You don’t even know which order the updates execute in, unless your Create implementation special cases Parent. This means the code may be fast or slow - you don’t know! All hail hash tables.
  2. You redundantly create temporary hash tables which causes more work for the garbage collector
  3. You redundantly fill the temporary hash tables with key/value pairs which incurs a CPU cost
  4. When filling object with values, you do dynamic property updates by name, which prevents some optimizations we’re planning to introduce

You can make it a tiny bit better by making sure your Create implementation is aware that Parent should be set last - you should at least do that if you really want to use this syntax. (RbxUtility.Create does not do that!) My recommendation though is to avoid extra allocations and extra CPU overhead and just stick to the manual approach - option 1 is the fastest.

453 Likes
Explosion not detecting part
Sound being heard globally despite setting MaxDistance
Ranks System Help
Set other properties before Parent after Instance.new
Creating a part to encase the player
How do I spawn a part and still have that part's child script with a Touched event work?
Best practices for designing for mobile users?
Attempt to Index Field Parent (a nil value)
Inventory System Issues (How can I fix it?)
Instance.new refers to parameter that doesn't exist and fails to mention deprecated parameter
Method Instance:Duplicate()
TweenService:Create no property named 'Position' for object 'OriginalPosition'
Help with my loop script
Build a Tetris type of game
Global Leaderboard Issue
How to create objects in a more optimized way?
Viewport Frame Parenting
Viewport Frame Parenting
Datastore loading times takes to long
Is there any way to improve this part generation script?
Camera script causes running animation
Which functions should I add to my ModuleScript?
Why is the text label not appearing in the list?
Why does this explosion part ignore the wait?
Which functions should I add to my ModuleScript?
Client CoreScripts GitHub Repository & Semi-Comprehensive Overview
Scripting a gui, don't understand error
My other remover script is also not working?
Part Just change the position to the place of appearance in the new
Weld rotation doesn't stay?
BodyThrust is having no effect at all
Keeping parts welded together
Raycasting Gun Spread
How to animate without Humanoid?
DataStore Not Working (Group Game)
DataStore Not Working (Group Game)
Why wont my punch damage script work?
Getting back into scripting
Leaderstats cant save
Most Efficient Way to Instance
Is there a more efficient way to shoot projectiles?
Most Efficient Way to Instance
Custom chat tags not working?
Trying to get swords to rotate properly in ViewportFrame
DataStore won't work at all
Memory leak help
How to make a system like this:
Raycast to mouse position and camera script not cooperating
Did local Create = LoadLibrary("RbxUtility").Create or local Create = loadstring("RbxUtility").Create. Can give it a backdoor?
DataStore not working?
My NPC periodically stops rotating unless I move it with studio tools?
Is it possible to save anything inside Folder?
Question about module efficiency?
Welding Lots of Parts to a Character
Having a problem with BodyForce
My bullets are curving for no apparent reason?
Problems with Motor6D
Welding player character to another character
Datastore/Leaderboard Problem
Tool Not Equipping When Picked Up
Datastore Script Help
Issue with constraint - Part moving around
LoadAnimation not working?
Datastore not working / not existing?
Hover GUI Occasionally Bugging Out
Get my custom chat to change between rainbow colors
Help with forcing a character to face a certain direction?
How do I weld a spherical mesh to the players head?
I can't create a folder for storing BoolValue in the player?
Building Tool Problem
RemoteEvent fires for every player in the server?
Is there a way to shorten this script?
Coin Leaderboard Issue[Fixed]
Tool is not positioned right even when animated
Need help With Sword Damage
Playing an animation into an NPC locally
Is there a way to put an instance into a low-memory state?
MVP Script, Guidance
How to make a pop-up shop GUI
Efficient way of executing code?
FastTouched v2 {Improved}
Rock Power Optimization
Hinge Constraints Lag
Help needed to store data (DataStore2)
Load Library Fix?
Adding Values into Table
Issue with XP not saving
Sound Won't Play
How to set multiple "Instance.New" parameters
String.Sub issue
Best Scripting Practices?
Offcentering a weld?
For loop isn't spawning parts as intended
What is wrong with this code?
Money gui doesn't working
Why is My Script Only focusing On 1 Player?
Part Editor Module
Rotating part Not sure how to do it
Help With Exp When You Touch a Part
Help with armor giver
Help with positioning part
How to identify players models?
Saving Instance like a players's house doesn't work
Script not changing color of part
Detect text in a textbox
ClickDetector Cash Script not working
Trail for a specific person
Motor6d Equipping Glitch
How to make a simple leaderboard by scripting
How to make it so when a player touches a part, they get a "coin"
Data isn't saving, Why?
Projectile lags for a second when I instantiate it
Instances not being created
Why won't this spawn a part where my mouse clicks?
Help with adding a part to a character
Attachments not appearing on CharacterAdded
So I just found out that the second parameter of Instance.new() is deprecated
Five weeks into scripting
Why is my GUI not showing the amount of cash?
Text Sequence Generation from Images
How do i make my instance spawn with a name?
Cloned tool from serverstorage doesn't work
Issue with the music system
Feedback on my first Instance.new code
How to make a simple Kill leaderboard (main problem finished)
Custom Grid Material
Why is instance.new() not working? Confused
How to add a stringValue to leaderboard
Personal variable
How can i let a dropper drop a model?
How can i let a dropper drop a model?
If statement won't run
How to fix my leaderstats
How to fix my leaderstats
Make two players face each other
Region3 tutorial (Easy Difficulty)
Performance considerations, improvements, and optimizations when making a game
Vehicle Spawning into Character vs into workspace
Throwing hammer is not working!
Sound System Glitch
How to make my equipping system work?
How would I be able to combine 6 DataStores in 1 DataStore?
NPC shooting parts not working? No errors
Is this a good way to replicate to all clients?
How to get the name of a variable?
Weld all players from a table to a part
Can someone explain all the events of run service in detail?
Datastore Script - Overhaul
Why does my part not position correctly?
Keeping ragdoll after death
.Changed event not firing
DataStore Scheme
Custom (Submarine-Like) Character Move System?
Region3 tutorial (Easy Difficulty)
Player Count Leaderstats
How do I stop this from constantly giving the player (x) item?
My House Script is very broken, with no logical explanation
Need help with script
Module doesnt run even without errors
FindPartsinRegion3 Not working (Please help) [ROBLOX API]
I'm a newbie to scripting, are there any bad habits or good habits I should be building?
Leaderboard not working
Give parent to an instance before giving it properties?
Script only working for one person
Value nil to everything?
Why isn't this code working?
WaitForChild() Issue
Antro | Aesthetic Intros for your game
Procedural Tiling w/ Applications in Chess, Plates, & Voxel Terrain Generation
Can't get a Bomb to Explode when a Tool is Activated
Is setting part properties through a script more efficient than through the “Properties” tab
Leader board problem
Create a folder when a player joins and destroy it when they leave
Instance.new refers to parameter that doesn't exist and fails to mention deprecated parameter
Prevent more items from spawning
Needing to change the position & orientation of a motor6d's part1
IntValue changes in Script but not in Game
What are some lines of code that will impact game's performance?
How would I give (x) cash every (y) seconds to every player?
Script Issue: Uniform Giver
Value Names not printing correctly
I call it "StarterLeaderstats"
Trying to Make a Visual Ray Cast but it isn't Working
IntValue not being created
[Improved] How to Actually Improve Performance in Your Games
Body Velocity isn't working
How do I make this work?
Remove Folder When Player Leave the game
BoolValue not setting to true in leaderstats
Night Vision Thing Won't Work
Is this correct for a kill/death leaderboard that saves?
How could I improve my Anti-Cheat?
DataStoreService Error
Hidden leaderstats source code
How to detect the first layer of a randomly generated map?
Why is my leaderstat preview name "Value"
[HELP] Arrest System Not Working
Why doesn't my tool data load instantly?
Part.Position = Mouse.Hit.P is going ANYWHERE but my mouse position. Help?
Should I set OnInvoke before or after setting Parent of a BindableFunction?
Tree Generator Module
Session Panel | Host Trainings With Ease
Begineer guide to scripting
Begineer guide to scripting
Motor6D won't go into characters UpperTorso
DataStore issue
Player data isn't always loading
Help with remote events
Which is correct?
When Part parented to Workspace it doesn't go where its supposed to go
Creating a custom service
Why is leaderstats not showing in studio?
Tool parenting to workspace instead of the folder I created
Need Help For Oxygen system
Need help with shop/currency datasave
Leaderstat not showing
Why does this gun script keep lagging my game?
Help with crack effect - cracks being created in the air
Using Instance.new("class", **parent**) on the **client** isn't that bad?
Launch Pad Scripting
IntValues won't move to leaderstats(Stats)
Can't get the player in a server script
Leaderstats String Value Help
Wierd script problem i guess
Instance Based Game Pass System
Motor6D scripting problem
AlignPosition acts after a long time
Clone vs Instance.new(), which one would be faster?
How do I make Instances appear where I want them to?
Built-In RbxUtility.Create
Instance.new() lag causing problem
Instance.new() won't work
CERTAIN LOCALSCRIPTS NOT WORKING - Events not firing?
How to To save Data - Tutorial
Messiest script ever
How to insert a Sound into Workspace using Scripts?
Boolvalue player assigning issue
How can I add datastore to this script?
How to add a boolvalue to a player when he/she joins?
Leaderstats Malfunctioning
In-game Log System Help
Is using Clone().Parent = x a bad practice?
Need help optimizing this code
Sword not appearing on players hand
How can I break a part into X amount of small pieces?
Need help with an obby air vent
Part isn't spawning
Help cleaning up / improving module
My Personal Library [Module]
Instance.new Needs Table?
Instance.old (recycle instances)
Attributez: Storing values/tables into objects! (READ SOLUTION)
Improving the code
Body Position Works After A While After Parenting It To Another Part But Works The Second Time
508: Exhausted all retries for key: %s DataStore
Get the Corners of a BasePart (No matter what shape)[W.I.P]
Basic bow and arrow
Morphing Issue Killing Player
Help with script
Shield is not working
Raycasting not showing up
Help with this Non-FE Datastore Script
Particles... Now in stunning 3D!
Network ownership to player works in Roblox studio but not in Roblox?
Problems with Overhead GUI
Leaderstats not showing the name in the topbar?

P.S. I am considering a script analysis warning that would warn against using Instance.new with a parent argument - it’s tantalizing but so very wrong to use it.

68 Likes

I was thinking “It can’t be that much of a difference can it?”

Oh my. Time to go change some things.

230 Likes

Good to know! By a stroke of luck, I frequently use my own implementation of util.Create, but it already does parent assignment last so properties are what they should be when ChildAdded/etc fire.

12 Likes

Oh.

Oh no.

EDIT: Never mind, as far as I can tell I had been taking this into account the whole time. whew

18 Likes

I’ve always done this. Now I’m justified! :smiley:

68 Likes

Time to start CTRL+F-ing

40 Likes

Oh dear, I was under the assumption that none of this would happen until the next frame. Better get round to some optimization.

6 Likes

Not to sound grumpy, but there’s a reason not to look into how ROBLOX gears works.
From a selected few gears, not only is there this sort of code, but also issues with people getting stuck with it, it’s not cross-platform compatible, and even invites to collision-related exploits for people to teleport outside of game barriers. Bites lip in disgust - that is, recode it all

:confounded:

19 Likes

A lot of CoreGui uses this, should probably look into cleaning those up.

20 Likes

You just gave me flashbacks on all the times I used the second/third method

24 Likes

Rip me, I used this at every chance I could. Guess now would be a good time to stop

13 Likes

I almost feel like I need to find something worse just to upset zeuxcg further.

Perhaps

Instance.new("Object")
  .Transparency(0)
  .Size(Vector3.new(2, 2, 2))
  .Anchored(true)
  .CFrame(CFrame.new(5, 5, 5))
  .BrickColor(BrickColor.new("Bright red"))
.Parent(workspace)

Through the use of a metatable.

Enjoy suffering some more @zeuxcg

do
	local create = Instance.new
	local object, parentFunction, setFunction, tab
	tab = setmetatable({}, {
		__index = function (_, index)
			if index == "Parent" then
				return parentFunction
			else
				return function (value)
					return setFunction(index, value)
				end
			end
		end
	})
	parentFunction, setFunction = function (parent)
		object.Parent = parent
		return object
	end, function (index, value)
		object[index] = value
		return tab
	end
	Instance = {
	new = function (class)
		object = create(class)
		return tab
	end
	}
end
17 Likes

NEVER NEVER NEVER read ROBLOX gear code.

Please.

90 Likes


I would, if it didn’t cause… this to appear.

4 Likes

Oh my

Time to go change all my instancing I guess

3 Likes

Tbh, if your game needs this kind of optimization…

@zeuxcg wouldn’t it be possible for the engine to only start doing the queueing for new objects when the thread yields?
E.g.

local obj = Instance.new("Part",workspace) -- state: instantiation
-- set properties
-- state: still instantiation
coroutine.yield() -- or wait()
-- thread yielded, obj's state becomes "initial setup" or "doing the queueing"
6 Likes

Oh my. I had observed this but I hadn’t connected it to setting properties after setting Parent. Good to know.

I’ve added a warning to the wiki documentation for Instance.new.

5 Likes

Reading ROBLOX Gear code is how I got started with Lua in the first place.

26 Likes

Keep in mind in his example the first 100 parts have already been created when the second for loop runs, it would be better to test fresh both times.

I just tried it (restarting each time) and got 0.0027530193328857 and 0.10685229301453 (40x difference) over 100 iterations, which is still a lot.

8 Likes