I have a class that has multiple computed properties.
The Getter looks up the value in a dictionary and then returns it.
The set stuff the dictionary.
I have a ‘bunch’ of these methods and I’d like to cache the value from the getter such that it doesn’t need to look it up but knows the value from the last call.
I believe I need to add two static properties for each of the getter/setters.
i.e.
Public Property mValue as integer
static cached as boolean = false
static cachevalue as integer
if cached then
return cachevalue
else
cachevalue = d.lookup(x)
cached = true
end if
return cachevalue
End Get
I am wondering if that static cached variable is scoped correctly. Wil each instance of this class have it’s own state?
Is
d.lookup(x) faster or slower than
the overhead of the using a Get method, following the cachedvalue exists route?
Doesn’t the code above mean that (in best Highlander tradition), there can only ever be ONE value - the first one you ask for?
After that cachedvalue exists and you will always get that return value?
Increasing slower when dictionaries gets bigger. Some keys can be random fast, others can be slower, and that’s a “random thing” depending on data distribution. Maybe that’s why he intended to cache. Some apps care about microseconds, because a lot of them saved gets the job done 5 minutes earlier, or some things just don’t work (correctly) if you don’t act at the allowed microseconds time band (hardware, protocols, etc).
But as Christian said, the design may be questionable, and maybe it just could not demand a dictionary.
I agree with others, this sounds like it may be a case of “premature optimization” where you go to a lot of work to save a little time, and it may not be necessary.
The general advice is: benchmark first, then optimize hotspots.
@Rick_Araujo is right that dictionary lookup can be slow in some cases, but in many cases it’s very fast and basically can be approximated as O(1) which means the time does not change based on the # of items in the dictionary. Technically it’s probably closer to O(log N) if the dictionary is using a balanced search tree.
By comparison. if you are using an Array, the time to find a value in the array (using Array.IndexOf ) does depend on the # of itmes and would be called O(N).
Dictionary uses a hash table, it means a jump NEAR the desired item and inspecting items until finding it to the end of its slot (bins) where it should be. So there’s a random number of steps. And the speed depends on size of slots, number of slots, hash algorithm, etc. And no, they don’t use trees for it.
My class has a few common keys implemented as properties, where the rest of the properties need to be looked up in the dictionary and processed…
I’m thinking
Private Property mmFileSize as UInt64
Private Property mInitDone as Boolean = false
Private Property d as Dictionary
Public Sub Constructor()
d = new Dictionary
...
For Each prop As Introspection.Introspection.PropertyInfo In Introspection.GetType(obj).GetProperties
// Check if the property has the CachedAttrib attribute
If prop.GetAttributes.Lookup("CachedAttrib") <> Nil Then
// Get the value of the property
Dim v As Variant = prop.Value(obj)
End If
Next
mInitDone = true
End Sub
Attributes ( CachedAttrib = true ) Public Property mFileSize as UInt64
Get
if mInitDone then
return mmFileSize
else
mmFileSize=d.lookup("FileSize")
end if
return mmFileSize
End Get
Set
mmFileSize = value
End Set
End Property
Introspection seems to me the only sensible way to initialize the cache.
I have a few dozen properties like this that need to be refactored into the new pattern.
Was there any thoughts on if the profiles I submitted ‘demonstrated’ an improvement?
Introspection is (almost) always the wrong solution. It’s slow and easy to break.
Caching the properties in the constructor is not ideal:
it means that you load all cacheable properties up front, even if they might be properties that are never used even once
the class instance can’t be used until all that loading is done.
Imagine your class is a Book and one method is GetFirstPage() and another method is GetPageCount(). It’s easy to imagine a situation where you would want the former, without the latter, but if you force all cached properties to be loaded in the Constructor, you are probably adding delay, rather than reducing it.
I use a system like this in one of my projects where multiple long-running instances may change the values. They’re stored in a SQLite database that is read at runtime and refreshed periodically in all instances, then written in the Setters.
There shouldn’t be any conflicts due to how I’m using this and values shouldn’t be very stale as they check a flagging database value every minute to see if they need to reload.
It sounds like a fragile system, but with the right checks it works well.
I could be off here, but unless you plan to expand that in the future:
Dictionary call without caching is …probably? fast enough. 64 slots isnt going to cause the hash table to wander about massively.
Optimum (to my mind) would be an array of 64 values, addressed using enums instead of dictionary keys.
eg
ArrayOfThings(enumBanana) //straight to it
rather than
DictionaryOfThings.value(“Banana”) //lookup or cached value
Yeah. Sadly, without sight of the actual code (this is the first mention of a loop with 524288 iterations), it’s hard to get our teeth into this.
Off topic, but relatable:
This morning I was dealing with a customer who I had asked to ‘delete a folder’
They replied ‘I did what you asked but I couldnt delete it, so I checked the box and it still doesnt work’
I have no idea what they were looking at, and so it is here.
Is there any chance that the code can be shown?
What are you doing inside these nested loops?
Do you cache any of the values they use?
The actual value is a string that gets converted to an integer in an operator convert method.
The discussion is actually, should i cache the value…
P.S. Clairvoyance is now a prerequisite to technical support.
Tell them to open a dos box in admin mode and then navigate to the folder and make sure no finder window is open in the folder that is trying to be deleted and that no one else is using it.