Optimizing plot grid system

How could I squeeze as much optimization out as possible in this module? It is a plot generation system that generates some plots and streams them out/in based on their position.
Module:

-- Services --

local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Modules --

local Serializer = require(ReplicatedStorage.Assets.Modules.RBLXSerialize)
local MapInfo = require(ReplicatedStorage.Assets.PlotMap)
local LoaderPool = require(ReplicatedStorage.Assets.Modules.LoaderPool)

local Pool = ReplicatedStorage

-- Types --

export type PlotInfo = {
	Model : Model,
	Chance : number,
	Size : Vector2
}

export type Entry = {
	x : number,
	y : number,
	sizex : number,
	sixey : number,
	rot : number,
	id : string,
	intercept : number,
	cf : CFrame,
	_instance : Model?
}

export type GenerateWorld = {
	Seed : Random,
	GridSizeX : number,
	GridSizeY : number,
	BaseSize : number,
	Plots : {[any] : PlotInfo},
	PlacedData : {Entry},
	SpawnPillars : boolean,
	CeilingHeight : number,

	new : (Seed : number?) -> GenerateWorld,
	GenerateAsync : (self : GenerateWorld) -> (),
	StreamAround : (self : GenerateWorld, Position : Vector3, radius : number) -> (),
	Serialize : (self : GenerateWorld, Model : Model) -> string,
	UnSerialize : (self : GenerateWorld, Id : string) -> Model
}

local GenerateWorld = {}
GenerateWorld.__index = GenerateWorld

local ROTS = {
	0,
	90,
	180,
	270
}

local function ChooseRandom(rng : Random, info : {PlotInfo}, size : Vector2?)

	local ndef = {}

	if size then

		for i, v in info do

			if v.Size == size then

				table.insert(ndef, v)

			end

		end

	else

		ndef = info

	end

	if #ndef == 0 then

		return MapInfo.EmptyPlot

	end

	local t = 0

	for _, e in pairs(ndef) do

		if type(e) == "number" then continue end

		t += e.Chance

	end

	local random = rng:NextNumber() * t

	for _, f in pairs(ndef) do

		if type(f) == "number" then continue end

		random -= f.Chance
		if random <= 0 then return f end

	end

	return MapInfo.EmptyPlot

end

local function Rotate(rng : Random)

	return ROTS[rng:NextInteger(1, #ROTS)]

end

local function IsPillarCell(self, x, y)

	if not self.SpawnPillars then return false end

	local spacing = MapInfo.Pillar.Spacing + 1
	local dx = (x - self._pillarOffsetX) % spacing
	local dy = (y - self._pillarOffsetY) % spacing
	return (dx == 0) and (dy == 0)

end

local function Check(self : GenerateWorld, x : number, y : number, sizex : number, sizey : number) : boolean

	if x < 1 or y < 1 or sizex < 1 or sizey < 1 then return false end
	if x + sizex - 1 > self.GridSizeX or y + sizey - 1 > self.GridSizeY then return false end

	for i = x, x + sizex - 1 do

		for j = y, y + sizey - 1 do

			if self.Occupied[i][j] then return false end

			if IsPillarCell(self, i, j) then return false end

		end

	end

	return true

end

local function Mark(self : GenerateWorld, x : number, y : number, sizex : number, sizey : number) : boolean

	for i= x, x + sizex - 1 do

		for j = y, y + sizey - 1 do

			self.Occupied[i][j] = true

		end

	end

end

local function Init(Folder : {Model} | Folder) : {PlotInfo}

	assert(Folder, "no folder!!!")

	local tbl = {}

	if type(Folder) == "table" then

		tbl = Folder

	else

		tbl = Folder:GetChildren()

	end

	if not tbl then return end

	local data = {}

	for i, v : PlotInfo in MapInfo do

		local check = v.Model

		for _, b in tbl do

			if check == b then

				table.insert(data, v)

			end

		end

	end

	return data

end

local function SetSpawns(rng : Random, Model : Model)

	for i, v in Model:GetDescendants() do

		if v:IsA("SpawnLocation") then

			if rng:NextNumber() > 0.2 then v:Destroy() continue end

			v.CanCollide = false
			v.CanQuery = false

			v.Parent = workspace.Map.Map.Spawns

		end

	end

end

local function CloneTable(tbl : {any}) : {any}

	local new = {}

	for i, v in tbl do

		table.insert(new, v)

	end

	return new

end

function GenerateWorld.new(seed, gridSize, plotlist, ceilingheight, spawnpillar)

	local self = setmetatable({}, GenerateWorld)

	self.Seed = seed and Random.new(seed) or Random.new()

	local grid = gridSize or Vector2.new(52,52)
	self.GridSizeX = math.clamp(grid.X, 1, 52)
	self.GridSizeY = math.clamp(grid.Y, 1, 52)

	self.BaseSize  = 27.6

	self.CeilingHeight = ceilingheight or 2020
	self.SpawnPillars = spawnpillar or true

	local spacing = MapInfo.Pillar.Spacing
	self._pillarOffsetX = self.Seed:NextInteger(1, spacing)
	self._pillarOffsetY = self.Seed:NextInteger(1, spacing)

	self.Occupied = {}

	for i=1,self.GridSizeX do

		self.Occupied[i] = {}

		for j=1,self.GridSizeY do

			self.Occupied[i][j] = false

		end

	end

	local size = 4
	local cols = math.ceil(self.GridSizeX / size)
	local rows = math.ceil(self.GridSizeY / size)
	local actor = require(script.ChunkActor)

	self.ChunkSize = size
	self.Chunks = {}

	for i = 1, cols do
		
		self.Chunks[i] = {}
		
		for j = 1, rows do
			
			local originX = (i-1)*size + 1
			local originY = (j-1)*size + 1
			
			self.Chunks[i][j] = actor.new(self, originX, originY, size)
			
		end
		
	end


	self.Plots = Init(plotlist) or { MapInfo.EmptyPlot }
	self.PlacedData = {}

	return self

end

function GenerateWorld:Finalize()

	for _, entry in ipairs(self.PlacedData) do
		
		local ci = math.floor((entry.x - 1) / self.ChunkSize) + 1
		local cj = math.floor((entry.y - 1) / self.ChunkSize) + 1
		local chunk = self.Chunks[ci] and self.Chunks[ci][cj]
		
		if chunk then
			
			chunk:Add(entry)
			
		end
		
	end

end

function GenerateWorld.ScalePillar(model : Model, height : number)

	local main = model:FindFirstChild("Pillar_Main", true)
	if not main or not main:IsA("BasePart") then return end

	local old = main.Size
	local new = Vector3.new(old.X, height, old.Z)
	local delta = (new.Y - old.Y)/2

	main.Size = new
	main.CFrame = main.CFrame * CFrame.new(0, delta, 0)

end

function GenerateWorld:Serialize(model)

	local id = Serializer.Encode(model, true)
	model:Destroy()

	return id

end

function GenerateWorld:UnSerialize(id)

	local model = Serializer.Decode(id, true)
	model.Parent = workspace.Map.Map

	return model

end

local function _GEN(self, x)

	local y = 1
	local spacing = MapInfo.Pillar.Spacing
	local rng = self.Seed

	while y <= self.GridSizeY do

		local info, rotation, sizex, sizey

		if IsPillarCell(self, x, y) then

			local new = CloneTable(MapInfo.Pillar)
			new.Spacing = nil
			info = ChooseRandom(rng, new)
			rotation, sizex, sizey = 0, 1, 1

			Mark(self, x, y, 1, 1)

		else

			info = ChooseRandom(rng, self.Plots)

			rotation = Rotate(rng)

			local swapped = (rotation == 90 or rotation == 270)
			sizex = swapped and info.Size.Y or info.Size.X
			sizey = swapped and info.Size.X or info.Size.Y

			if not Check(self, x, y, sizex, sizey) then

				y += 1
				continue

			end

			Mark(self, x, y, sizex, sizey)

		end

		local clone = info.Model:Clone()

		GenerateWorld.ScalePillar(clone, self.CeilingHeight)

		local worldx = ((x-1) + sizex/2) * self.BaseSize
		local worldy = ((y-1) + sizey/2) * self.BaseSize
		local bottom = clone:GetExtentsSize().Y/2

		local cf = CFrame.new(worldx, bottom, worldy) * CFrame.Angles(0, math.rad(rotation), 0)
		clone:PivotTo(cf)

		SetSpawns(rng, clone)

		local id = self:Serialize(clone)
		local intercept = math.sqrt((sizex*self.BaseSize/2)^2 + (sizey*self.BaseSize/2)^2)

		table.insert(self.PlacedData, {
			x         = x,
			y         = y,
			rot       = rotation,
			id        = id,
			cf        = cf,
			intercept = intercept,
			_instance = nil,
		})

		y += sizey

	end

end

function GenerateWorld:GenerateAsync()

	for x=1,self.GridSizeX do

		task.wait(0.1)

		task.spawn(_GEN, self, x)

	end

	for x=1,self.GridSizeX do

		for y=1,self.GridSizeY do

			if not self.Occupied[x][y] then

				self.Occupied[x][y] = true

				local info = ChooseRandom(self.Seed, self.Plots, Vector2.new(1,1))

				local clone = info.Model:Clone()
				local rot = Rotate(self.Seed)

				local worldx = ((x-1)+.5)*self.BaseSize
				local worldy = ((y-1)+.5)*self.BaseSize
				local bottom = clone:GetExtentsSize().Y/2

				local cf = CFrame.new(worldx,bottom,worldy)*CFrame.Angles(0,math.rad(rot),0)
				clone:PivotTo(cf)

				local id = self:Serialize(clone)
				local intercept = math.sqrt((1*self.BaseSize/2)^2 + (1*self.BaseSize/2)^2)

				table.insert(self.PlacedData, {
					x = x, 
					y = y, 
					rot = rot,
					id = id, 
					cf = cf,
					intercept = intercept,
					_instance = nil
				})

			end

		end

	end

end

function GenerateWorld:StreamAround(position, radius)

	local gridx = math.clamp(math.floor(position.X / self.BaseSize) + 1, 1, self.GridSizeX)
	local gridy = math.clamp(math.floor(position.Z / self.BaseSize) + 1, 1, self.GridSizeY)

	local ci = math.floor((gridx - 1) / self.ChunkSize) + 1
	local cj = math.floor((gridy - 1) / self.ChunkSize) + 1

	local r = math.ceil(radius / (self.BaseSize * self.ChunkSize))

	for i = ci - r, ci + r do
		
		for j = cj - r, cj + r do
			
			local chunk = self.Chunks[i] and self.Chunks[i][j]
			
			if chunk then
				
				chunk:Update(position, radius)
				
			end
			
		end
		
	end

end

return GenerateWorld

Also im having problems with the actor, as sometimes it leaves plots behind for seemingly no reason? (basically they dont deload), and sometimes plots that should load dont?
actor:

-- Services --

local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Modules --

local LoaderPool    = require(ReplicatedStorage.Assets.Modules.LoaderPool)
local GenerateWorld = require(ReplicatedStorage.Assets.Modules.GenerateWorld)

-- Main --

local ChunkActor = {}
ChunkActor.__index = ChunkActor

function ChunkActor.new(world : GenerateWorld, originx : number, originy : number, size)
	
	local self = setmetatable({}, ChunkActor)
	
	self._world = world
	self.originx = originx
	self.originy = originy
	self.size = size
	self.entries = {}
	
	return self
	
end

function ChunkActor:Add(entry)
	
	entry._world = self._world
	table.insert(self.entries, entry)
	
end

function ChunkActor:InRange(wpos, radius, entry)
	
	local pos = entry.cf.Position
	local Distance = (Vector3.new(pos.X, wpos.Y, pos.Z) - wpos).Magnitude
	
	return Distance <= (radius + entry.intercept)
	
end

function ChunkActor:LoadEntry(e)
	
	LoaderPool.Enqueue(function()
		
		local inst = e._world:UnSerialize(e.id)
		
		if e._world.SpawnPillars and e.sizex == 1 and e.sizey == 1 then
			
			e._world.ScalePillar(inst, e._world.CeilingHeight)
			
		end
		
		inst:PivotTo(e.cf)
		inst.Parent = workspace.Map.Map
		e._instance = inst
		
	end)
	
end

function ChunkActor:UnloadEntry(e)
	
	LoaderPool.Enqueue(function()
		
		e.id = e._world:Serialize(e._instance)
		e._instance:Destroy()
		e._instance = nil
		
	end)
	
end

function ChunkActor:Update(pos, radius)
	
	for _, e in ipairs(self.entries) do
		
		local range = self:InRange(pos, radius, e)
		
		if range and not e._instance then
			
			self:LoadEntry(e)
			
		elseif not range and e._instance then
			
			self:UnloadEntry(e)
			
		end
		
	end
	
end

return ChunkActor

thank you for the help