Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Implement multi-dispatch cache.
While selftest is dominated by startup time, this does shave around 8% off the time to run it.
- Loading branch information
Showing
6 changed files
with
230 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package org.perl6.nqp.sixmodel.reprs; | ||
|
|
||
| import org.perl6.nqp.runtime.ThreadContext; | ||
| import org.perl6.nqp.sixmodel.REPR; | ||
| import org.perl6.nqp.sixmodel.STable; | ||
| import org.perl6.nqp.sixmodel.SerializationReader; | ||
| import org.perl6.nqp.sixmodel.SixModelObject; | ||
| import org.perl6.nqp.sixmodel.TypeObject; | ||
|
|
||
| public class MultiCache extends REPR { | ||
| public SixModelObject type_object_for(ThreadContext tc, SixModelObject HOW) { | ||
| STable st = new STable(this, HOW); | ||
| SixModelObject obj = new TypeObject(); | ||
| obj.st = st; | ||
| st.WHAT = obj; | ||
| return st.WHAT; | ||
| } | ||
|
|
||
| public SixModelObject allocate(ThreadContext tc, STable st) { | ||
| MultiCacheInstance obj = new MultiCacheInstance(); | ||
| obj.st = st; | ||
| return obj; | ||
| } | ||
|
|
||
| public SixModelObject deserialize_stub(ThreadContext tc, STable st) { | ||
| throw new RuntimeException("MultiCache does not participate in serialization"); | ||
| } | ||
|
|
||
| public void deserialize_finish(ThreadContext tc, STable st, | ||
| SerializationReader reader, SixModelObject obj) { | ||
| throw new RuntimeException("MultiCache does not participate in serialization"); | ||
| } | ||
| } |
179 changes: 179 additions & 0 deletions
179
src/org/perl6/nqp/sixmodel/reprs/MultiCacheInstance.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| package org.perl6.nqp.sixmodel.reprs; | ||
|
|
||
| import org.perl6.nqp.runtime.CallSiteDescriptor; | ||
| import org.perl6.nqp.sixmodel.SixModelObject; | ||
| import org.perl6.nqp.sixmodel.TypeObject; | ||
|
|
||
| public class MultiCacheInstance extends SixModelObject { | ||
| private static final int MD_CACHE_MAX_ARITY = 4; | ||
| private static final int MD_CACHE_MAX_ENTRIES = 16; | ||
| private static final int MD_CACHE_INT = 1; | ||
| private static final int MD_CACHE_NUM = 2; | ||
| private static final int MD_CACHE_STR = 3; | ||
|
|
||
| private SixModelObject zeroArity; | ||
| private ArityCache[] arityCaches = new ArityCache[MD_CACHE_MAX_ARITY]; | ||
|
|
||
| private class ArityCache | ||
| { | ||
| /* The number of entries we have in the cache. */ | ||
| public int numEntries; | ||
|
|
||
| /* This is a bunch of ST hashes, with natives special-cased. We allocate | ||
| * it arity * MAX_ENTRIES big and go through it in arity sized chunks. */ | ||
| public long typeIds[]; | ||
|
|
||
| /* Whether the entry is allowed to have named arguments. Doesn't say | ||
| * anything about which ones, though. Something that is ambivalent | ||
| * about named arguments to the degree it doesn't care about them | ||
| * even tie-breaking (like NQP) can just throw such entries into the | ||
| * cache. Things that do care should not make such cache entries. */ | ||
| public boolean namedOK[]; | ||
|
|
||
| /* The results we return from the cache. */ | ||
| public SixModelObject[] results; | ||
| } | ||
|
|
||
| public void add(CallCaptureInstance capture, SixModelObject result) { | ||
| /* If there's flattenings, we can't cache. */ | ||
| if (capture.descriptor.hasFlattening) | ||
| return; | ||
|
|
||
| /* If it's zero arity, just stick it in that slot. */ | ||
| Object[] args = capture.args; | ||
| if (args.length == 0) { | ||
| this.zeroArity = result; | ||
| return; | ||
| } | ||
|
|
||
| /* Count number of positional args and build type tuple. */ | ||
| int numArgs = 0; | ||
| byte[] argFlags = capture.descriptor.argFlags; | ||
| long argTup[] = new long[MD_CACHE_MAX_ARITY]; | ||
| boolean hasNamed = false; | ||
| for (int i = 0; i < argFlags.length; i++) { | ||
| switch (argFlags[i]) { | ||
| case CallSiteDescriptor.ARG_INT: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return; | ||
| argTup[numArgs++] = MD_CACHE_INT; | ||
| break; | ||
| case CallSiteDescriptor.ARG_NUM: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return; | ||
| argTup[numArgs++] = MD_CACHE_NUM; | ||
| break; | ||
| case CallSiteDescriptor.ARG_STR: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return; | ||
| argTup[numArgs++] = MD_CACHE_STR; | ||
| break; | ||
| case CallSiteDescriptor.ARG_OBJ: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return; | ||
| long flag = ((long)((SixModelObject)args[i]).st.hashCode()) << 1; | ||
| if (!(args[i] instanceof TypeObject)) | ||
| flag |= 1; | ||
| argTup[numArgs++] = flag; | ||
| break; | ||
| default: | ||
| if ((argFlags[i] & CallSiteDescriptor.ARG_FLAT) != 0) | ||
| return; | ||
| hasNamed = true; | ||
| } | ||
| } | ||
|
|
||
| /* If the cache is saturated, don't do anything (we could instead do a random | ||
| * replacement). */ | ||
| ArityCache ac = this.arityCaches[numArgs - 1]; | ||
| if (ac != null && ac.numEntries == MD_CACHE_MAX_ENTRIES) | ||
| return; | ||
|
|
||
| /* If there's no entries yet, need to do some allocation. */ | ||
| if (ac == null) { | ||
| ac = new ArityCache(); | ||
| ac.typeIds = new long[numArgs * MD_CACHE_MAX_ENTRIES]; | ||
| ac.namedOK = new boolean[MD_CACHE_MAX_ENTRIES]; | ||
| ac.results = new SixModelObject[MD_CACHE_MAX_ENTRIES]; | ||
| this.arityCaches[numArgs - 1] = ac; | ||
| } | ||
|
|
||
| /* Add entry. */ | ||
| int insType = ac.numEntries * numArgs; | ||
| for (int i = 0; i < numArgs; i++) | ||
| ac.typeIds[insType + i] = argTup[i]; | ||
| ac.results[ac.numEntries] = result; | ||
| ac.namedOK[ac.numEntries] = hasNamed; | ||
| ac.numEntries++; | ||
| } | ||
|
|
||
| public SixModelObject lookup(CallCaptureInstance capture) { | ||
| /* If there's flattenings, we can't use the cache. */ | ||
| if (capture.descriptor.hasFlattening) | ||
| return null; | ||
|
|
||
| /* Count number of positional args and build type tuple. */ | ||
| int numArgs = 0; | ||
| Object[] args = capture.args; | ||
| byte[] argFlags = capture.descriptor.argFlags; | ||
| long argTup[] = new long[MD_CACHE_MAX_ARITY]; | ||
| boolean hasNamed = false; | ||
| for (int i = 0; i < argFlags.length; i++) { | ||
| switch (argFlags[i]) { | ||
| case CallSiteDescriptor.ARG_INT: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return null; | ||
| argTup[numArgs++] = MD_CACHE_INT; | ||
| break; | ||
| case CallSiteDescriptor.ARG_NUM: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return null; | ||
| argTup[numArgs++] = MD_CACHE_NUM; | ||
| break; | ||
| case CallSiteDescriptor.ARG_STR: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return null; | ||
| argTup[numArgs++] = MD_CACHE_STR; | ||
| break; | ||
| case CallSiteDescriptor.ARG_OBJ: | ||
| if (numArgs >= MD_CACHE_MAX_ARITY) | ||
| return null; | ||
| long flag = ((long)((SixModelObject)args[i]).st.hashCode()) << 1; | ||
| if (!(args[i] instanceof TypeObject)) | ||
| flag |= 1; | ||
| argTup[numArgs++] = flag; | ||
| break; | ||
| default: | ||
| if ((argFlags[i] & CallSiteDescriptor.ARG_FLAT) != 0) | ||
| return null; | ||
| hasNamed = true; | ||
| } | ||
| } | ||
|
|
||
| /* If it's zero-arity, return result right off. */ | ||
| if (numArgs == 0) | ||
| return hasNamed ? null : this.zeroArity; | ||
|
|
||
| /* Look through entries. */ | ||
| ArityCache ac = this.arityCaches[numArgs - 1]; | ||
| if (ac == null) | ||
| return null; | ||
| int tPos = 0; | ||
| for (int i = 0; i < ac.numEntries; i++) { | ||
| boolean match = true; | ||
| for (int j = 0; j < numArgs; j++) { | ||
| if (ac.typeIds[tPos + j] != argTup[j]) { | ||
| match = false; | ||
| break; | ||
| } | ||
| } | ||
| if (match) { | ||
| if (hasNamed == ac.namedOK[i]) | ||
| return ac.results[i]; | ||
| } | ||
| tPos += numArgs; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } |