How do I reduce my script's lag?

I have a script that constantly scans over a folder named game.Workspace.Blocks every 0.1 seconds.
while task.wait(0.1) do
Now, the thing is, game.Workspace.Blocks can contain user created data. This can include over 50k+ parts. And as such, it lags.
The script does things such as make new instances, delete old ones, etc…
Here’s a segment of it:

if game.Workspace.Blocks:FindFirstChild("Blocks") then
		local a = game.Workspace.Blocks:FindFirstChild("Blocks")
		if #a:GetChildren() ~= 0 then
			for _, child in pairs(a:GetChildren()) do
				child.Parent = game.Workspace.Blocks
			end
		else
			a:Destroy()
		end
	end
	local cache = game.Workspace.Blocks:GetChildren()
	for _, child in pairs(cache) do
		for _, child2 in ipairs(child:GetChildren()) do
			if string.match(child2.Name, "CannonProperty_") then
				if not child:FindFirstChild("CannonApplied_" .. string.sub(child2.Name, 16, 99999)) then
					local side = string.sub(child2.Name, 16, 99999)
					local bool = Instance.new("BoolValue")
					bool.Parent = child
					bool.Name = "CannonApplied_" .. side
					coroutine.wrap(function()
						while true do
							if not child or not child:IsDescendantOf(game.Workspace.Blocks) then
								break
							end
							if not child2 or not child2:IsDescendantOf(child) then
								break
							end
							if child:FindFirstChild("CannonProperty_" .. side) then
								if side == "Right" and child and child2 then
									local leftDirection = Vector3.new(-1, 0, 0)
									child.AssemblyLinearVelocity = leftDirection * -child2.Value
									task.wait(0.1)
								elseif side == "Left" and child and child2 then
									local leftDirection = Vector3.new(-1, 0, 0)
									child.AssemblyLinearVelocity = leftDirection * child2.Value
									task.wait(0.1)
								elseif side == "Front" and child and child2 then
									local leftDirection = Vector3.new(0, 0, 1)
									child.AssemblyLinearVelocity = leftDirection * -child2.Value
									task.wait(0.1)
								elseif side == "Back" and child and child2 then
									local leftDirection = Vector3.new(0, 0, 1)
									child.AssemblyLinearVelocity = leftDirection * child2.Value
									task.wait(0.1)
								elseif side == "Top" and child and child2 then
									local leftDirection = Vector3.new(0, 1, 0)
									child.AssemblyLinearVelocity = leftDirection * child2.Value
									task.wait(0.1)
								elseif side == "Bottom" and child and child2 then
									local leftDirection = Vector3.new(0, 1, 0)
									child.AssemblyLinearVelocity = leftDirection * -child2.Value
									task.wait(0.1)
								end
							else
								break
							end
						end
					end)()
				end
			end
			coroutine.wrap(function()
				local side = string.sub(child2.Name, 26, 99999)
				if string.match(child2.Name, "EffectFocalPointProperty_") 
					and not child:FindFirstChild("EffectFocalPointPropertyApplied_" .. side) then
					local otherval = child:FindFirstChild("EffectFocalPointProperty_" .. side).Value
					local addedvalue = Instance.new("BoolValue")
					addedvalue.Value = true
					addedvalue.Parent = child
					addedvalue.Name = "EffectFocalPointPropertyApplied_" .. side
					local fx = child:WaitForChild("BlockViewPropertiesEffect_" .. side, 5)
					if not fx then
						print("[JANITOR] BlockViewPropertiesEffect_" .. side .. " was not found in time!")
					else
						fx.Name = "Effect"
						local z = Instance.new("Attachment")
						z.Parent = child
						fx.Parent = z
						z.Name = "BlockViewPropertiesEffect_" .. side
						z.CFrame = CFrame.new(otherval)
					end
				end
			end)()
		end
		if child:FindFirstChild("IceProperty") and not child:FindFirstChild("IceApplied") then
			local ice = Instance.new("BoolValue")
			ice.Name = "IceApplied"
			ice.Parent = child
			if not child.CustomPhysicalProperties then
				local defaultFriction = 0.3
				local defaultElasticity = 0.5
				local defaultDensity = 1
				local defaultFrictionWeight = 1
				local defaultElasticityWeight = 1
				child.CustomPhysicalProperties = PhysicalProperties.new(
					defaultDensity, 
					defaultFriction, 
					defaultElasticity, 
					defaultFrictionWeight, 
					defaultElasticityWeight
				)
			end

			local currentProps = child.CustomPhysicalProperties
			child.CustomPhysicalProperties = PhysicalProperties.new(
				currentProps.Density, 
				0,
				currentProps.Elasticity, 
				100,
				currentProps.ElasticityWeight
			)
		elseif child:FindFirstChild("IceApplied") and not child:FindFirstChild("IceProperty") then
			child:FindFirstChild("IceApplied"):Destroy()
			child.CustomPhysicalProperties = nil
		end
		if child:FindFirstChild("WeldBreakerProperty") and not child:FindFirstChild("WeldBreakerApplied")then
			local applied = Instance.new("BoolValue")
			applied.Name = "WeldBreakerApplied"
			applied.Parent = child
			child.Touched:Connect(function(obj)
				if obj:FindFirstChild("WeldPropertyApplied") and obj:FindFirstChild("WeldProperty") 
					and not game.ReplicatedStorage.Files.Temp:FindFirstChild("WorldLoading") then
					local link = obj:FindFirstChild("WeldProperty").Value
					for _, obj2 in pairs(game.Workspace.Blocks:GetChildren()) do
						if obj2:FindFirstChild("WeldPropertyLink") then
							if obj2:FindFirstChild("WeldPropertyLink").Value == link
							then
								obj:FindFirstChild("WeldPropertyApplied"):Destroy()
								obj:FindFirstChild("WeldProperty"):Destroy()
								obj2:WaitForChild("WeldLinkApplied"):Destroy()
								obj2:WaitForChild("WeldPropertyLink"):Destroy()
								for _, decal in pairs(obj2:GetChildren()) do
									if decal.Name == "WeldDecal" and decal:IsA("Decal") then
										decal:Destroy()
									end
								end
								for _, decal in pairs(obj:GetChildren()) do
									if decal.Name == "WeldDecal" and decal:IsA("Decal") then
										decal:Destroy()
									end
								end
							end
						end
					end
				end
				if obj:FindFirstChild("WeldLinkApplied") and obj:FindFirstChild("WeldPropertyLink") 
					and not game.ReplicatedStorage.Files.Temp:FindFirstChild("WorldLoading") then
					local link = obj:FindFirstChild("WeldPropertyLink").Value
					for _, obj2 in pairs(game.Workspace.Blocks:GetChildren()) do
						if obj2:FindFirstChild("WeldProperty") then
							if obj2:FindFirstChild("WeldProperty").Value == link
							then
								obj2:FindFirstChild("WeldPropertyApplied"):Destroy()
								obj2:FindFirstChild("WeldProperty"):Destroy()
								obj:WaitForChild("WeldLinkApplied"):Destroy()
								obj:WaitForChild("WeldPropertyLink"):Destroy()
								for _, decal in pairs(obj2:GetChildren()) do
									if decal.Name == "WeldDecal" and decal:IsA("Decal") then
										decal:Destroy()
									end
								end
								for _, decal in pairs(obj:GetChildren()) do
									if decal.Name == "WeldDecal" and decal:IsA("Decal") then
										decal:Destroy()
									end
								end
							end
						end
					end
				end
			end)
		end
		if child:FindFirstChild("MTeleProperties") 
			and not child:FindFirstChild("MTeleApplied")
			and not child:IsA("Folder")
		then
			local bool = Instance.new("BoolValue")
			bool.Parent = child
			bool.Name = "MTeleApplied"
			bool.Value = true
			local mteleprop = child:FindFirstChild("MTeleProperties")
			local connection33
			connection33 = child.Touched:Connect(function(hit)
				if not child:FindFirstChild("MTeleProperties") then connection33:Disconnect() return end
				if hit:FindFirstChild("MovableRespawnProperty") or hit:FindFirstChild("SpawnArea") then
					hit.Position = mteleprop.Value
				end
			end)
		end

This still lags the game a lot, and this script is pretty important. How could I fix this?
The game:

Some additional footage of me trying to edit with around 50000 instances:

I have a script that iterates through a folder with 50k instances every 0.1 seconds

How about you just don’t? I’m like 95% sure you are able to achieve whatever you want to achieve without having to check every single instance’s properties every tenth of a second. (Although I’m not that invested to read the entire codeblock and find out for sure)

I NEED TO.
I use a serialization system to load things, and some things in the serialization system do not save. Such as PhysicalProperties, Touched events, etc.
Without this script, the properties won’t refresh at all.

I mean, why not just use remote events then…?


I just explained this!!
I’m using a serialization system that loads from the server!
It needs to apply the properties to the block one way or another, because everything doesn’t just automatically save!

Well, as I said above, I’ll include something you also said. You NEED these things to happen, but you DON’T NEED it to be done this way. The problem is definitely that while 0.1s is executing a huge function and you WILL NEED to change it to another way.

Could you explain better what the idea behind your code is that checks every 0.1s? I believe that your brief explanation will be easier to think of a way to help you than reading your code and trying to understand your idea on my own. Why should it check every 0.1s? What is this actually changing within the workspace?

My code uses a serialization system, and said system can’t save things like velocity, connections, PhysicalProperties, etc…
So, every second, to keep updated, it does that.

I don’t understand how you wouldn’t be able to save physical properties of a part, because they can be done that way.

local Properties = workspace.Part.CurrentPhysicalProperties

local Density = Properties.Density
local Elasticity = Properties.Elasticity
local ElasticityWeight = Properties.ElasticityWeight
local Friction = Properties.Friction
local FrictionWeight = Properties.FrictionWeight 

print(Density, Elasticity, ElasticityWeight, Friction, FrictionWeight)

Nevermind, I fixed it myself.

filler

For your cannons, you can tag them with CollectionService, and use :GetTagged() to get a list of blocks with a cannon property, instead of looping through every blocks
Even better, I think you can make this whole thing event driven. Instead of updating AssemblyLinearVelocity from a loop, you can update it when the value of child2 changes

For the others as well, it seems like you have a repeating pattern in your code of checking if some parts have values and stuff as a child, and if not, instantiate them, which should probably instead be event driven, and instantiated when the command/effect is added to the block


What is your serialization system? I don’t see how the script you provided is related to a serialization system. Do you serialize by saving everything to a datastore, or through CreatePlaceAsync() and :SavePlaceAsync()?


Was your game built with Openate? I’ve played Blockate for some time, and made my own block building game (although it is dead and never got any traction)

In my game (and any project I work on), I take the approach of modulating scripts, I find it very effective at keeping projects manageable and avoiding massive scripts that are hard to navigate. I can provide more detail if you are interested