GenericCache - An Instance Caching ModuleScript

GenericCache:

Module: Model


Information:

GenericCache is a ModuleScript used for instance caching / pooling. GenericCache allows for the creation of multiple GenericCache objects which are both fast and extremely customizable.

Instance caching / pooling is a technique used to efficiently reuse objects. Rather than frequently creating and destroying objects with .new() and :Destroy(), systems like GenericCache store unused objects in a list for future use. Caching will always be helpful in keeping your game fast, but only really start to improve performance when you are dealing with hundreds or thousands or .new() or :Destroy calls a second. This reduction in overhead from frequent object creation and destruction makes modules like GenericCache useful in various scenarios ranging from particle management to bullet systems.


Features:

Create GenericCache Objects:

  • Store Custom Types or Instances
  • Pass arguments to constructors and use template instances
  • Preload an initial number of objects into the cache
  • Set a size limit for the cache
  • Automatically run a “reset” method on all objects when they are added to the cache

Update GenericCache Instance Data:

  • Dynamically change the maximum size of the cache
  • Modify the “reset” method triggered when objects are added

Check the Contents of GenericCache Objects:

  • View the object that will be returned next
  • Use a predicate method to check if the cache contains a specific object

Add, Remove, and Retrieve Objects to and from GenericCache Objects:

  • Insert one object manually or add multiple objects in bulk
  • Remove individual objects or all objects match a predicate function
  • Retrieve the first available object or the first that matches a predicate function

Clear and Destroy GenericCache Objects:

  • Clear the contents of the cache for garbage collection
  • Destroy the cache and its contents for garbage collection

Performance:

GenericCache is highly optimized (I have spent hours on this :upside_down_face:) and demonstrates fast runtimes across the board. The table below outlines performance metrics for different types of GenericCache objects. This includes objects used for custom types, which are faster, and objects used for instances, which are slower.

Performance Table
-- Example GenericCache Objects
local testClass = require()
local testPart = Instance.new("IntValue")
testPart.Value = 10

GenericCache.new(testClass, {}, 10)
GenericCache.new(Instance, testPart, 10)
Function / Method Arguments Average runtime (ÎĽs)
.new() 1 * 10-6 - 2 * 10-5
:SetMaxSize() 10 2.5 * 10-7 - 2 * 10-6
:SetResetOnAddition() true, true 3 * 10-7
:SetResetMethodName() “reset”, true 3 * 10-7
:PeekObject() 8 * 10-9 - 1 * 10-8
:ContainsObject() 1.5 * 10-8
:AddObject() 2 * 10-8 - 6 * 10-8
:AddObjects() 10 1 * 10-7 - 2 * 10-6
:RemoveObject() 2.5 * 10-8 - 5.5 * 10-8
:RemoveObjects() 1 * 10-7 - 5 * 10-7
:RetrieveObject() 1 * 10-8 - 5 * 10-8
:RetrieveObjectCustom() 1.5 * 10-8 - 7.5 * 10-8
:ClearCache() 7.5 * 10-9 - 3 * 10-7
:Destroy() 2 * 10-8 - 4 * 10-7

Usage:

Creating Caches

Creating a GenericCache Object:

The first step is requiring the module and instantiating anything you will use in the constructor, in this case, a template instance.

-- GenericCache should be stored in RPS so both the server and client can use it --
local GenericCache = require(game:GetService("ReplicatedStorage").GenericCache)

local testPart = Instance.new("IntValue")
testPart.Value = 10

After this, you will want to create an object using GenericCache.new() and pass in any arguments. Any arguments other than objectClass and objectInput are optional and will be set to their default values specified in the Settings table if not passed into the function .

  • The objectClass must have a .new() method unless objectInput is a template instance
  • Setting the maxSize value to -1 will cause the cache to have no maximum size
local objectClass = Instance
local objectInput = testPart
-- default values for GenericCache Objects --
local initialSize = 10
local maxSize = -1
local resetOnAddition = false
local resetMethodName = "reset"

local testCache = GenericCache.new(objectClass, objectInput, initialSize, maxSize, resetOnAddition, resetMethodName)
Updating Instance Data

Updating GenericCache Instance Data:

There are three methods that can be used to manipulate instance data: :SetMaxSize(), :SetResetOnAddition(), and :SetResetMethodName(). These methods are all quite simple and require basic arguments.

  • The applyResetMethod argument will apply the current reset method to all objects in the cache at the time of the method being called (if resetOnAddition is set to true)
testCache:SetMaxSize(5)
-- default values for applyResetMethod --
testCache:SetResetOnAddition(true, false)
testCache:SetResetMethodName("reset", false)
Checking Cache Contents

Checking the Contents of a GenericCache Object:

There are two methods used to check the contents of a cache, and those are :PeekObject() and :ContainsObject(). Both are very simple and only :ConstainsObject requires input (an object to check for)

testCache:PeekObject()
testCache:ContainsObject(testPart)
Adding, Removing, and Retrieving Objects

Adding Objects to a GenericCache:

Adding objects is relatively simple and requires either an object to be added or a number of template object copies to be added

  • Template objects are not added to their GenericCache objects automatically
  • Using :AddObjects() will either create # copies of the template object or call objectClass.new() # times
testCache:AddObject(testPart)
testCache:AddObjects(10)

Removing Objects from a GenericCache

Removing objects is about as easy as adding them with only a bit more complexity added on. For the removing methods, you either need to input a reference to a specific object that needs to be removed or a predicate function that determines if an object is removed.

  • All objects that match the predicate function will be removed from the cache
  • The predicate function must return either true or false depending on if the object needs to be removed (true for removal)
testCache:RemoveObject(testPart)
testCache:RemoveObjects(function(cachedObject)
      if cachedObject.value == 10 then
            return true
      end
      return false
end)

Retrieving Objects from a GenericCache

Retrieving Objects is probably the easiest of the three major cache manipulations, and mainly consists of method calls with no arguments. For :RetrieveObjectCustom(), you do need to input another predicate method which is used to find the first object that matches your requirements.

  • These methods will only return one method, which is different than the removal methods
  • The predicate method here is similar to that of :RemoveObjects() in its return format
testCache:RetrieveObject()
testCache:RetrieveObjectCustom(function(cachedObject)
      if cachedObject.value > 10 then
            return true
      end
      return false
end)
Clearing and Destroying Caches

Clearing and Destroying GenericCache Objects:

Clearing and Destroying GenericCache objects is extremely easy and only requires a method call.

  • Objects stored in the cache will only be GCed if there are no references to them in other code
  • calling :Destroy() on GenericCache objects will also call :ClearCache()
testCache:ClearCache()
testCache:Destroy()

Other Info:

I am currently working on a more in depth documentation, but for now, this post should have everything you need to get started with the module. Feel free to reply with any questions you have, and I will try to answer them as soon as I can.

4 Likes

image
Found an error…

Thats weird. If the object was created correctly then _scriptCache should be a table. Have you deleted or changed the value of _scriptCache?

no i havnt, but the object which i wanna clone is an attachment

I think the issue may be that the attachments cant be parented to the folder the cache uses, but I am not sure why this would cause _scriptCache to be nil. For now I would suggest trying to replace anywhere it it says object.Parent = _workspaceCache to object.Parent = nil. I will try to get a fix for this out soon.

1 Like