GenericCache - An Instance Caching ModuleScript

GenericCache:

Module: Model | Plugin: Plugin


Information:

GenericCache is an instance-caching ModuleScript that allows for the creation and management of multiple caching objects. The module is designed to be highly customizable and fast, making it a great choice in various situations.

Instance-caching is a technique for efficiently reusing objects. Rather than repeatedly creating and destroying objects with .new() and :Destroy(), GenericCache stores unused objects in a table for future use. While instance-caching is always beneficial for maintaining game performance, its advantages become particularly noticeable when dealing with hundreds or thousands of operations per second. The reduction of overhead associated with the frequent creation and destruction of objects makes GenericCache useful in a wide range of scenarios ranging from particle management to gun systems.


Features:

Create GenericCache Objects:

  • Store custom types or Instances
  • Pass arguments to constructors or use a template instance
  • Preload an initial number of objects into the cache
  • Set a size limit for the cache
  • Automatically run a reset method on any objects added to the cache

Update GenericCache Instance Data:

  • Dynamically change the maximum size of the cache
  • Change whether the reset method is called on objects added
  • Modify the reset method called on objects that are added

Check the Contents of GenericCache Objects:

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

Add, Remove, Return, and Retrieve objects to and from GenericCache objects:

  • Manually insert sets of objects or load them in bulk
  • Return or retrieve the next object or objects that match a predicate function
  • Remove specific objects from the cache.

Clear, Reset, and Destroy GenericCache Objects:

  • Clear the contents of the cache for garbage collection
  • Reset the cache to its default state
  • Destroy the cache, including its metatable and contents

Performance:

GenericCache is highly optimized and boasts fast runtimes across the board. The table below provides the performance metrics for most of the ModuleScript’s methods, categorized by the use of custom types or Instances. Differences between the two stem from the additional overhead associated with Instances compared to more lightweight, script-based types.

Runtime Table

All runtimes for the table represent the average execution time over 1000 test runs following an initial warm-up of 100 test runs to take advantage of JIT compilation. For methods with varying outputs based on their input arguments, testing was performed using more basic objects unless otherwise specified. Brackets in the arguments column signify different sets of arguments and ranges in the runtime column signify the difference in runtimes between caches storing custom types and those storing Instances.

-- Example GenericCache Objects
local testClass = require(testClass)
GenericCache.new(testClass, {}) – custom type cache
GenericCache.new(Instance, {"IntValue"}) – Instance cache
Function / Method Arguments Average runtime (s)
.new() TestObject, {} 1.5E-7
:SetMaxSize() 10 5E-8
:SetResetOnAddition() true, true 2E-7
:SetResetMethodName() “Reset”, true 2E-7
:ContainsObject() TestObject.new() 1E-7
:LoadObjects() 10 7.5E-7 - 7E-6
:AddObject() TestObject.new() 1E-7 - 2E-7
:AddObjects() 10 3E-7 - 8.5E-7
:ReturnObject() 5E-8
:ReturnCustomObject() function(object) return true end 8.5E-8
:ReturnCustomObjects() function(object) return true end 6.5E-7
:RemoveObject() TestObject.new() 1.5E-7
:RetrieveObject() 6E-8
:RetrieveCustomObject() function(object) return true end 1E-7
:RetrieveCustomObjects() function(object) return true end 6.5E-7
:ClearCache() 6E-8 - 8E-8
:Reset() 7E-8 - 9E-8
:Destroy() 1E-7

Usage:

Creating Caches

Creating a GenericCache Object:

The first step to using GenericCache is requiring the module and instantiating anything that will be used in the constructor. In this case, that includes a template Instance.

-- GenericCache should be stored in RPS so both the server and client can access it --
local GenericCache = require(game:GetService("ReplicatedStorage").GenericCache)
local testObject = Instance.new("IntValue")
testObject.Value = 10

After completing the previous step, you will want to create a new GenericCache object using GenericCache.new() with your arguments. Any arguments other than objectConstructor and objectInput are optional and will be set to their default values if not passed into the constructor.

  • The objectConstructor argument must include a function or method at the index “new” unless objectInput is a template Instance
  • Setting the value of maxSize to -1 will result in the cache having an unlimited size
local objectConstructor = Instance
local objectInput = testObject

-- default values for GenericCache Objects --
local initialSize = 0
local maxSize = -1
local resetOnAddition = false
local resetMethodName = "Reset"
local newCache = GenericCache.new(objectConstructor, objectInput, initialSize, maxSize, resetOnAddition, resetMethodName)
Updating Instance Data

Updating GenericCache Instance Data:

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

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

Checking the Contents of a GenericCache Object:

Both :ContainsObject() and any methods beginning with the keyword “return” can be used to check the contents of a GenericCache object without editing it. :ContainsObject() will search the cache for a specific object, while the return methods will return either the next available object or any objects matching the predicate function passed to them.

  • The predicate function must return either true or false depending on if the object inputted needs to be returned (true for removal)
testCache:ContainsObject(testPart)
testCache:ReturnObject()
testCache:ReturnCustomObject(function(object) return true end)
testCache:ReturnCustomObjects(function(object) return true end)
Adding, Removing, and Retrieving Objects

Adding Objects to a GenericCache:

Adding objects to a GenericCache object is very simple and requires either a single object or set of objects to be manually added with either :AddObject() or :AddObjects. You can also load a set number of template Instances into the cache with `:LoadObjects().

  • Using :LoadObjects() will either create objectCount copies of the template Instance or call objectClass.new() objectCount times
testCache:AddObject(object)
testCache:AddObjects({objectOne, objectTwo})
testCache:LoadObjects(10)

Removing & Retrieving Objects from a GenericCache

Removing objects from a GenericCache object is just as easy as adding them to it. For removing a specific object, you can use :RemoveObject(), and for retrieving objects, you can use any of the methods beginning with the keyword “retrieve”. Retrieving a custom set of objects requires a predicate function similar to the methods beginning with “return”.

  • All objects that match the predicate function will be removed from the cache and returned
  • The predicate function must return either true or false depending on if the object needs to be retrieved (true for removal)
testCache:RemoveObject(object)
testCache:RetrieveObject()
testCache:RetrieveCustomObject(function(object) return true end)
testCache:RetrieveCustomObjects(function(object) return true end)
Clearing, Resetting, and Destroying Caches

Clearing and Destroying GenericCache Objects:

Clearing, resetting, and destroying GenericCache objects is as easy as typing in the method name. :Clear() clears the cache, :Reset() resets it, and :Destroy() destroys it, simple as that.

  • Objects stored in the cache will only be GCed if there are no references to them in other code
testCache:Clear()
testCache:Reset()
testCache:Destroy()

Other Info:

I am still working on a more in-depth documentation, but it might be a while until it comes out (I’m lazy), 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

[Update] GenericCache Version 1.0

Module: Model | Plugin: Plugin


I decided to stop procrastinating and now GenericCache version 1.0 is here!

Key Changes:

  • Reworked the cache system for Instances by removing _workspaceCache
  • Renamed and / or added various new functions to make returning, removing, and retrieving items from the cache easier
  • Added a :Reset() method
  • Fixed the runtime table in the original post (wrong units)
  • Various bug fixes and minor reworks

Other Announcements:

GenericPlugin Release

GenericPlugin has been released!

Its a bit early, but I plan on making more ModuleScripts in the future and I wanted to make something that allows you to import any of them very easily (e.g. I don’t want to use Wally), and GenericPlugin is just that.

You can read more about GenericPlugin when its documentation page is released (hopefully in the near future), but it is pretty self explanatory and not really necessary at the moment.

Possible Signal Module

GenericSignal is in the works

I have started to work on a new custom signal ModuleScript that implements GenericCache. While the module is not completed, the basic concept is done, and it has proven to be very fast. Hopefully the module will be released before the end of the year, and the documentation for it will release at about the same time as the documentation for this module.

I will be working on fixing any bugs I find over the next week. As I said previously, if you find any bugs or have any requests for the next update, please reply to the post with that info.