Loop through workspace descendants without lag

for the newer scripters that are experiencing lag spike issues when looping through workspace descendants

workspace:GetDescendants() is expensive, as it loops through the children of each workspace object, and then loops through their children, and loops through the childrens children, so on so forth.

heres some code to fix those lag spikes

local WorkspaceDescendants = workspace:GetDescendants()
local Added, Removed;
local Enabled;

local function DescendantAdded(object)
	table.insert(WorkspaceDescendants, object)
end

local function DescendantRemoving(object)
	for k, v in ipairs(WorkspaceDescendants) do
		if v == object then
			table.remove(WorkspaceDescendants, k)
			break
		end
	end
end

local function Disable()
	if Enabled then
		Enabled = nil

		Added:Disconnect()
		Removed:Disconnect()

		Added = nil
		Removed = nil
	end
end

local function Enable()
	if Enabled then
		Disable()
	end
	
	Enabled = true
	Added = workspace.DescendantAdded:Connect(DescendantAdded)
	Removed = workspace.DescendantRemoving:Connect(DescendantRemoving)
end

Enable()
for _, v in ipairs(WorkspaceDescendants) do
	--...
end

edit
my code only calls getdescendants once, and then i rely on the variable which reduces lag and makes performance better, unlike workspace:getdescendants(), which doesnt use any caching and lags when you loop through a bunch of parts

4 Likes

I can’t tell if this is an actual resource as it has 0 explanation.

local function DescendantRemoving(object)
	for k, v in ipairs(WorkspaceDescendants) do
		if v == object then
			table.remove(WorkspaceDescendants, k)
		end
	end
end

This should break when it finds and removes the object as well or you’re wasting time looping through a table which you know doesn’t have the value

Furthermore:

workspace:getdescendants but it doesnt cause a lagspike everytime you call it in a game with a lot of parts

ok so you’re a jerk, however a smart jerk so i respect you a lot
image

:smiling_face_with_three_hearts:
my code only calls getdescendants once, and then i rely on the variable which reduces lag and makes performance better
unlike workspace:getdescendants(), which doesnt use any caching

This code is better for performance if you are calling GetDescendants a reasonable amount of times, however if you only plan to use it once then obviously one get descendants call is faster.

There isn’t anything inherit my wrong with this code, and while I would favour table.find for readability, anyone saying this resource is pointless or worse for performance is inherently wrong.

All of this being said, if you are calling GetDescendants() on workspace I would really be questioning if it’s a valid use case that couldn’t just be optimised with collection service.

6 Likes

I cleaned and added types to the original code

Code
local WorkspaceDescendants = workspace:GetDescendants()
 
local function descendantAdded(instance: Instance)
    table.insert(WorkspaceDescendants, instance)
end
 
local Array = {}
 
function Array.remove(array, value): any?
    local index = table.find(array, value)
    if index then
        return table.remove(array, index)
    end
end
 
local function descendantRemoving(instance: Instance)
    Array.remove(WorkspaceDescendants, instance)
end
 
workspace.DescendantAdded:Connect(descendantAdded)
workspace.DescendantRemoving:Connect(descendantRemoving)
 
return WorkspaceDescendants

this is meant to be used as a modulescript instead so it can be accessed by any script.

I removed the enable / disable functionality because it doesn’t seem to be necessary


@dellghtfuI you can definitely expand this to be more functional keep track of any Instance, not just workspace. It’d probably get more praises :slightly_smiling_face:

3 Likes