russellsprouts
Joined 13 years ago
Joined 13 years ago
In Halide, you specify the algorithm at a high level, then specify the "schedule" (execution order, vectorization, storage) separately. Then, it verifies that the two match. For tight loops like image kernels, it's often counter-intuitive which execution plan will give the best performance, so being able to experiment with the guarantee that you are still implementing the algorithm correctly is valuable.
For a database, it would be as if you could send a high level SQL query and an execution plan together.
Halide is very specific, but I could imagine a general-purpose language with a Python-like high level representation for code behavior, with the option to specify a lower-level implementation with a lot of performance annotations.
This reminds me of how Forth `if` and `then` can be compiled in a single pass -- `if` writes the branch instruction and pushes the current instruction pointer to the stack, and `then` pops it and patches the instruction to point to the current instruction pointer.
The inline assembly integration looks nice too -- accessing the arguments and return address as fields on the function makes a lot of sense.
import {Something} from './file';
console.log(Something.PROP);
TypeScript doesn't know what to emit without type information. If Something is a class, then the JS will look the same. But if it's a const enum, then TypeScript has to erase the Something.PROP expression and replace it with the constant value of that enum member, since Something will not exist at runtime.Const enums are erased at compile time. If you have a reference to `MyEnum.VAR`, TS has to check whether the enum is a const enum, and if so, replace with something like `1 /* VAR */`. This means that the type information in one file (where the enum is defined) is necessary to determine the proper output of any file that uses it.
See my other comment for details: https://news.ycombinator.com/item?id=20960710
There are only brief glimpses in the video, but here's what that looks like: https://imgur.com/a/AkOAzgF
The 14 left-right powered rail tracks are the music channels. Each vertical slice is a tick of music, and they are grouped into 9, the max length of a powered rail signal. The presence of an observer block underneath the rail indicates a note. The whole machine appears to be 128 x 14 bits. It has a serial read interface.
In more detail:
From the bottom of the image to the top you see:
- A row of redstone blocks, pistons, and redstone wire.
- A row of repeaters facing north
- A row of repeaters facing west
- An observer facing north
- 14 east-west powered rail/observer wires, sending signals to the east.
- In a layer underneath the east-west observer-rail wires, alternating columns of rails and redstone wire facing north-south. (Not visible in the picture)
- (A mirror image of the bottom)
The west facing repeaters control the current read location. The machine sends a signal until they all turn on, then waits until they all turn off, then turns them on again, etc. When one of the repeaters toggles between on and off, the north-facing observer above it sends a pulse. The pulse travels upwards in the north-south rail (in the invisible lower layer), and then if an observer is present, the signal is sent east along the east-west line for the channel.
The bottom two rows form a pause mechanism. The redstone block, piston, redstone wire row is an instant repeater line. When the north-facing repeaters are turned on, they will lock the west-facing repeaters in whatever state they are in. Using instant wire ensures the pause will happen immediately.
The whole system is mirrored at the top since the north-south rails in the lower layer can only send a signal up to 9 blocks. With read signals coming from both sides, this design could support up to 18 channels.
One last detail -- one column of music is read every 2 redstone ticks, so most of the repeaters are set to a 2 tick delay. However, the signal for a channel must be repeated using an observer every 9 blocks (adding a 1 tick delay), so the first repeater in each 9 block section is set to 1 tick. This compensates for the delay.
EDIT: I originally stated that there were 14 channels, but I overlooked that it's double-layered, and that there's a similar quad-layered system on the other side. There are 50 channels in total.
[0]: http://grammar.ccc.commnet.edu/grammar/twain.htm
[1]: http://www.lettersofnote.com/2012/05/iorz-feixfuli-m-j-yilz....
See: Automatic Generation of Peephole Superoptimizers, Sorav Bansal and Alex Aiken, ASPLOS 2006. https://theory.stanford.edu/~aiken/publications/papers/asplo...
My implementation: https://github.com/RussellSprouts/6502-enumerator
I did something related in the past: https://github.com/RussellSprouts/6502-enumerator. It uses C++ templates to share an emulator implementation between z3-powered symbolic execution and actual execution. It was meant to find equivalence between random instruction sequences for peephole optimization, rather than optimizing a specific input sequence.
Shadow instructions are very interesting and cursed. I've seen them used in very limited circumstances for NOP-slides for timing code: https://www.pagetable.com/?p=669. It would be fun to see it applied in otherwise normal code. My enumerator wouldn't support this -- it didn't execute actual 6502 instructions from bytes -- it had its own internal representation for `the first arbitrary absolute pointer` or `the second arbitrary immediate constant`. These would either be initialized with random concrete values or z3 variables.