PSA: Don't use with parent argument

I’ve discovered a pretty bad performance issue in one of top games that has to do with 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 =‘type’); fill obj fields
  2. local obj =‘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:

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 ="Part")
wall.Size =, 10, 1)
wall.CFrame =, 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 called with parent argument:

local wall ="Part", workspace.Effects)
wall.Size =, 10, 1)
wall.CFrame =, 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 =, 10, 1);
CFrame =, 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.

Explosion not detecting part
Sound being heard globally despite setting MaxDistance
Creating a part to encase the player
Set other properties before Parent after
Ranks System Help
How do I spawn a part and still have that part's child script with a Touched event work?
Attempt to Index Field Parent (a nil value)
How can i let a dropper drop a model?
How can i let a dropper drop a model?
Personal variable
How to add a stringValue to leaderboard
Why is not working? Confused
Custom Grid Material
Why is my GUI not showing the amount of cash?
Instances not being created
Motor6d Equipping Glitch
Issue with the music system
GUI that says leaderstat value
Text Sequence Generation from Images
Attachments not appearing on CharacterAdded
Help with adding a part to a character
Why won't this spawn a part where my mouse clicks?
Attributez: Storing values/tables into objects!
How to make it so when a player touches a part, they get a "coin"
How to make a simple leaderboard by scripting
Trail for a specific person
[Help Please] Bomb
ClickDetector Cash Script not working
Detect text in a textbox
Script not changing color of part
Saving Instance like a players's house doesn't work
Rotating part Not sure how to do it
Help with positioning part
Help with armor giver
Help With Exp When You Touch a Part
Why is My Script Only focusing On 1 Player?
Part Editor Module
Offcentering a weld?
Best Scripting Practices?
String.Sub issue
Money gui doesn't working
What is wrong with this code?
For loop isn't spawning parts as intended
So I just found out that the second parameter of is deprecated
Adding Values into Table
Issue with XP not saving
Leaderstats cant save
Network ownership to player works in Roblox studio but not in Roblox?
How to make a simple Kill leaderboard (main problem finished)
Sound Won't Play
Is there a way to put an instance into a low-memory state?
MVP Script, Guidance
How to make a pop-up shop GUI
Tool is not positioned right even when animated
Is it possible to save anything inside Folder?
Get the Corners of a BasePart (No matter what shape)[W.I.P]
Load Library Fix?
Help needed to store data (DataStore2)
Hinge Constraints Lag
Playing an animation into an NPC locally
Need help With Sword Damage
Coin Leaderboard Issue[Fixed]
RemoteEvent fires for every player in the server?
Welding Lots of Parts to a Character
Datastore Script Help
Having a problem with BodyForce
My bullets are curving for no apparent reason?
Question about module efficiency?
Most Efficient Way to Instance
Most Efficient Way to Instance
My NPC periodically stops rotating unless I move it with studio tools?
How to make a system like this:
Building Tool Problem
I can't create a folder for storing BoolValue in the player?
How do I weld a spherical mesh to the players head?
Help with forcing a character to face a certain direction?
Get my custom chat to change between rainbow colors
Hover GUI Occasionally Bugging Out
Datastore not working / not existing?
LoadAnimation not working?
Issue with constraint - Part moving around
Tool Not Equipping When Picked Up
Datastore/Leaderboard Problem
Problems with Motor6D
Memory leak help
DataStore not working?
DataStore Not Working (Group Game)
DataStore Not Working (Group Game)
How to animate without Humanoid?
Why wont my punch damage script work?
Datastore loading times takes to long
Did local Create = LoadLibrary("RbxUtility").Create or local Create = loadstring("RbxUtility").Create. Can give it a backdoor?
Raycast to mouse position and camera script not cooperating
Custom Script Commands [Module]
DataStore won't work at all
Trying to get swords to rotate properly in ViewportFrame
Custom chat tags not working?
Is there a more efficient way to shoot projectiles?
Thoughts on my 3D perlin noise script?
Getting back into scripting
Part Just change the position to the place of appearance in the new
Raycasting Gun Spread
Keeping parts welded together
Weld rotation doesn't stay?
Which functions should I add to my ModuleScript?
Scripting a gui, don't understand error
Which functions should I add to my ModuleScript?
My other remover script is also not working?
Why does this explosion part ignore the wait?
Client CoreScripts GitHub Repository & Semi-Comprehensive Overview
Camera script causes running animation
Is there any way to improve this part generation script?
Build a Tetris type of game
Is setting part properties through a script more efficient than through the “Properties” tab
Inventory System Issues (How can I fix it?)
Best practices for designing for mobile users?
Viewport Frame Parenting
Viewport Frame Parenting
Global Leaderboard Issue
Performance considerations, improvements, and optimizations when making a game
TweenService:Create no property named 'Position' for object 'OriginalPosition'
Leaderstats not showing the name in the topbar?
Problems with Overhead GUI
508: Exhausted all retries for key: %s DataStore
Method Instance:Duplicate() refers to parameter that doesn't exist and fails to mention deprecated parameter refers to parameter that doesn't exist and fails to mention deprecated parameter
Built-In RbxUtility.Create
Particles... Now in stunning 3D!
Help with script
Basic bow and arrow
Morphing Issue Killing Player
Help with this Non-FE Datastore Script
Raycasting not showing up
Shield is not working
Help with my loop script
Projectile lags for a second when I instantiate it
Throwing hammer is not working!
Vehicle Spawning into Character vs into workspace
Make two players face each other
How to fix my leaderstats
How to fix my leaderstats
How do i make my instance spawn with a name?(SOLVED)
What do you guys think of my UI Handling module?
If statement won't run

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


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

Oh my. Time to go change some things.


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.



Oh no.

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


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


Time to start CTRL+F-ing


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


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



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


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


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


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

  .Size(, 2, 2))
  .CFrame(, 5, 5))
  .BrickColor("Bright red"))

Through the use of a metatable.

Enjoy suffering some more @zeuxcg

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




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


Oh my

Time to go change all my instancing I guess

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?

local obj ="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"

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


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


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.