Add files via upload

This commit is contained in:
TimothyLottes
2016-11-21 22:44:07 -05:00
committed by GitHub
parent 0341a0ffb3
commit d3d7e3f286
2 changed files with 183 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
<html><head><link rel="stylesheet" href="style.css"></head><body><div class="page">
<h1>20151113 - Rethinking the Symbolic Dictionary</h1>
<br>
<i>Another permutation of dictionary implementation for forth like languages...</i><br>
<br>
<b>Source</b>
<br>
Exported source is composed of two parts,<br>
<br>
(1.) Token array, where tokens can reference a local symbol by index into local hash table.<br>
(2.) Local symbol hash table, has string for each entry.<br>
<br>
Strings are 64-bits maximum and are stored in a reversible nearly pre-hashed form.
So hashing of a string is just an AND operation.
Tokens are 32-bits.
Local symbol hash is after the token array, so it can be trivially discarded after import.<br>
<br>
<b>Global Dictionary</b>
<br>
Global dictionary maps 32-bit index to 32-bit value.
Each 32-bit index has an associated 64-bit string stored in the same reversible nearly pre-hashed form.
Dictionary entries are allocated by just taking the next entry in a line.
There is no deletion. Just two arrays (32-bit value array, and 32-bit string array), and an index for the top.
<br>
<br>
<b>Source Import</b>
<br>
Starts with loaded source in memory and allocated space for one extra array,<br>
<br>
(1.) Source token array, gets translated to loaded-in-memory form.<br>
(2.) Source local symbol hash table, with each entry being a 64-bit string.<br>
(3.) Remap space, extra zeroed array with a 32-bit value per entry in local hash table.<br>
<br>
Import streams through the global dictionary,
checking for a match in the source's local symbol hash table.
Upon finding a match, it writes the global index for the symbol into the associated remap space entry.
Import next streams through the source token array,
replacing the local symbol index
with the global index from the remap space entry.
When the remap space entry is zero,
a new symbol is allocated in the global dictionary
(this involves adding a symbol to the end of the dictionary,
and coping over the string from the local symbol hash table to the global dictionary string array).
After import the local symbol hash table and remap space are discarded.
<br>
<br>
This solves many of the core problems from a more conventional design where the global dictionary is a giant hash table.
That conventional design suffers from bad cache locality (because of the huge hash table).
This new design maintains a cache packed global dictionary (no gaps).
That conventional design can have worst case first load behavior,
each initial lookup of a new word in the dictionary on load would miss through to DRAM,
adding 100 ns per lookup.
This new design is composed of either linear streaming operations for big data
(global dictionary, source token array, etc) all of which get hardware auto-prefetch.
The source local symbol hash table is expected to be not too big and easily stay in cache (the only thing with random access).
<br>
<br>
Note with this new design, interpreting source at run-time no longer has any hash lookup,
just a direct lookup.<br>
<br>
<b>First Source Import</b>
<br>
First source import (after machine reboot) has effectively an empty dictionary,
so import can be optimized.<br>
<br>
<b>Editing</b>
<br>
Edit time operations, such as find the index for an existing symbol,
check if a symbol already exists,
or tab complete a symbol,
is done via a full stream through the global dictionary string table.
This is a linear operation with full auto-prefetch, so expected to be quite fast in practice.
Edit time operations are limited by human factors, so not a problem.
<br>
<br>
<b>Source Export</b>
<br>
Source export requires first checking how many unique symbols are in the chunk of source.
Use a bit array with one bit per global dictionary entry.
Zero the bit array.
Stream through the chunk of source tokens and check for a clear bit in the bit array.
For each clear bit, set the bit, and advance the count of unique words.
<br>
<br>
Setup space for the local symbol hash.
Scale up the unique symbol count to make sure the hashing is efficient.
Pad up to the next power of 2 in size.
Stream through the source tokens,
using the token index to get a global dictionary string,
hash the string into the local symbol hash, writing the associated string if new entry,
and remapping the source token index to the local hash.
<br>
<br>
Export is the most complex part of the design, but still quite simple.
</div></body></html>
+77
View File
@@ -0,0 +1,77 @@
<html><head><link rel="stylesheet" href="style.css"></head><body><div class="page">
<h1>20151121 - ISA Toolbox</h1>
<br>
For years now I have found that nearly everything I work on can be made better by leveraging ISA features which are not always exposed in all the graphics APIs.
For example, currently working on a project now which could use the combination of the following,<br>
<br>
(1.) From <a href="https://www.opengl.org/registry/specs/AMD/shader_trinary_minmax.txt">AMD_shader_trinary_minmax</a>, max3().
Direct access to max of three values in a single V_MAX3_F32 operation.
If the GPU has 3 read ports on the register file for FMA, might at well take advantage of that for min/max/median.
AMD's DX driver shader compiler automatically optimizes these cases, for example "min(x,min(y,z))" gets transformed to "min3(x,y,z)".
<br>
<br>
(2.) Direct exposure of V_SIN_F32 and V_COS_F32, which have a range of +/- 512 PI and take normalized input.
Avoids and extra V_MUL_F32 and V_FRACT_F32 per operation.
Nearly all the time I use sin() or cos() I'm in range (no need for V_FRACT_F32).
Nearly all the time I'm in the {0 to 1} range for 360 degrees,
and need to scale by 2 PI only so code generation can later scale back by 1/2 PI.
Portable fallback for machines without V_SIN_F32 and V_COS_F32 like functionality looks like,
<br>
<br>
<tt>float sinNormalized(float x) { return sin(x * 2.0 * PI); }<br>
float cosNormalized(float x) { return cos(x * 2.0 * PI); }</tt><br>
<br>
(3.) Branching if any or all of the SIMD vector want to do something.
Massively important tool to avoid divergence.
For example in a full screen triangle, if any pixel needs the more complex path,
just have the full SIMD vector only do the complex path instead of divergently processing both complex and simple.
API can be quite simple,
<br>
<br>
<tt>
bool anyInvocations(bool x)<br>
bool allInvocations(bool x)</tt><br>
<br>
Example of how these could map in GCN (these scalar instructions execute in parallel with vector instructions, so low cost),<br>
<br>
<tt>// S_CMP_NEQ_U64 x,0<br>
// S_CBRANCH_SCCNZ<br>
if(anyInvocations(x)) { }<br>
<br>
// S_CMP_EQ_U64 x,-1<br>
// S_CBRANCH_SCCNZ<br>
if(allInvocations(x)) { }</tt><br>
<br>
(4.) Quad swizzle for fragment shaders for cross-invocation communication is super useful.
Given a 2x2 fragment quad as follows,<br>
<br>
<tt>01<br>
23</tt><br>
<br>
These functions would be quite useful (they map to DS_SWIZZLE_B32 in GCN),<br>
<br>
<tt>// Swap value horizontally.<br>
type quadSwizzle1032(type x)<br>
<br>
// Swap value vertically.<br>
type quadSwizzle2301(type x)</tt><br>
<br>
For example one could simultaneously write out the results of a fragment shader to the standard full screen pass
and write out the 1/2 x 1/2 resolution next smaller mip level at the same time using an extra image store.
Just use the following to do a 2x2 box filter in the shader,<br>
<br>
<tt>boxFilterColor = quadSwizzle1032(color) + color;<br>
boxFilterColor += quadSwizzle2301(boxFilterColor);</tt><br>
<br>
</div></body></html>