group the old and new ideas into misc

typed (file) still needs work, but at least the typed directory is gone
This commit is contained in:
Torsten Ruger
2018-04-11 20:53:49 +03:00
parent 4dcfddb270
commit a42ca6e514
12 changed files with 80 additions and 91 deletions

View File

@ -4,5 +4,3 @@
%li= link_to "Parfait", "parfait.html"
%li= link_to "Debugger", "debugger.html"
%li= link_to "Memory" , "memory.html"
%li= link_to "Threads" , "threads.html"
%li= link_to "Optimisation ideas" , "optimisations.html"

View File

@ -1,75 +0,0 @@
= render "pages/rubyx/menu"
%h1=title "Optimisation ideas"
%p I wont manage to implement all of these idea in the beginning, so i just jot them down.
%h3#avoid-dynamic-lookup Avoid dynamic lookup
%p This off course is a broad topic, which may be seen under the topic of caching. Slightly wrongly though in my view, as avoiding them is really the aim. Especially for variables.
%h4#i---instance-variables I - Instance Variables
%p Ruby has dynamic instance variables, meaning you can add a new one at any time. This is as it should be.
%p
But this can easily lead to a dictionary/hash type of implementation. As variable “lookup” is probably
%em the
most
common thing an OO system does, that leads to bad performance (unneccessarily).
%p
So instead we keep variables layed out c++ style, continous, array style, at the address of the object. Then we have
to manage that in a dynamic manner. This (as i mentioned
= succeed ")" do
%a{:href => "memory.html"} here
%p
When a new variable is added, we create a
%em new
Type and change the Type of the object. We can do this as the Type will
determine the Class of the object, which stays the same. The memory page mentions how this works with constant sized objects.
%p So, Problem one fixed: instance variable access at O(1)
%h4#ii---method-lookup II - Method lookup
%p Off course that helps with Method access. All Methods are at the end variables on some (class) object. But as we cant very well have the same (continuous) index for a given method name on all classes, it has to be looked up. Or does it?
%p
Well, yes it does, but maybe not more than once: We can conceivably store the result, except off course not in a dynamic
structure as that would defeat the purpose.
%p
In fact there could be several caching strategies, possibly for different use cases, possibly determined by actual run-time
measurements, but for now I just destribe a simeple one using Data-Blocks, Plocks.
%p
So at a call-site, we know the name of the function we want to call, and the object we want to call it on, and so have to
find the actual function object, and by that the actual call address. In abstract terms we want to create a switch with
3 cases and a default.
%p
So the code is something like, if first cache hit, call first cache , .. times three and if not do the dynamic lookup.
The Plock can store those cache hits inside the code. So then we “just” need to get the cache loaded.
%p Initializing the cached values is by normal lazy initialization. Ie we check for nil and if so we do the dynamic lookup, and store the result.
%p
Remember, we cache Type against function address. Since Types never change, were done. We could (as hinted above)
do things with counters or robins, but that is for later.
%p
Alas: While Types are constant, darn the ruby, method implementations can actually change! And while it is tempting to
just create a new Type for that too, that would mean going through existing objects and changing the Type, nischt gut.
So we need change notifications, so when we cache, we must register a change listener and update the generated function,
or at least nullify it.
%h3#inlining Inlining
%p
Ok, this may not need too much explanation. Just work. It may be intersting to experiment how much this saves, and how much
inlining is useful. I could imagine at some point its the register shuffling that determines the effort, not the
actual call.
%p Again the key is the update notifications when some of the inlined functions have changed.
%p
And it is important to code the functions so that they have a single exit point, otherwise it gets messy. Up to now this
was quite simple, but then blocks and exceptions are undone.
%h3#register-negotiation Register negotiation
%p
This is a little less baked, but it comes from the same idea as inlining. As calling functions is a lot of register
shuffling, we could try to avoid some of that.
%p More precisely, usually calling conventions have registers in which arguments are passed. And to call an “unknown”, ie any function, some kind of convention is neccessary.
%p
But on “cached” functions, where the function is know, it is possible to do something else. And since we have the source
(ast) of the function around, we can do things previouly imposible.
%p One such thing may be to recompile the function to acccept arguments exactly where they are in the calling function. Well, now that its written down. it does sound a lot like inlining, except without the inlining:-)
%p
An expansion if this idea would be to have a Negotiator on every function call. Meaning that the calling function would not
do any shuffling, but instead call a Negotiator, and the Negotiator does the shuffling and calling of the function.
This only really makes sense if the register shuffling information is encoded in the Negotiator object (and does not have
to be passed).
%p
Negotiators could do some counting and do the recompiling when it seems worth it. The Negotiator would remove itself from
the chain and connect called and new receiver directly. How much is in this i couldnt say though.

View File

@ -1,70 +0,0 @@
= render "pages/rubyx/menu"
%h1= title "Threads are broken"
%p
Having just read about rubys threads, i was moved to collect my thoughts on the topic. How this will influence implementation
i am not sure yet. But good to get it out on paper as a basis for communication.
%h3#processes Processes
%p
I find it helps to consider why we have threads. Before threads, unix had only processes and ipc,
so inter-process-communication.
%p
Processes were a good idea, keeping each programm save from the mistakes of others by restricting access to the processes
own memory. Each process had the view of “owning” the machine, being alone on the machine as it were. Each a small turing/
von neumann machine.
%p
But one had to wait for io, the network and so it was difficult, or even impossible to get one process to use the machine
to the hilt.
%p
IPC mechnisms were and are sockets, shared memory regions, files, each with their own sets of strengths, weaknesses and
apis, all deemed complicated and slow. Each switch encurs a process switch and processes are not lightweight structures.
%h3#thread Thread
%p
And so threads were born as a lightweight mechanisms of getting more things done. Concurrently, because when the one
thread is in a kernel call, it is suspended.
%h4#green-or-fibre Green or fibre
%p
The first threads that people did without kernel support, were quickly found not to solve the problem so well. Because as any
thread is calling the kernel, all threads stop. Not really that much won one might think, but wrongly.
%p
Now that Green threads are coming back in fashion as fibres they are used for lightweight concurrency, actor programming and
we find that the different viewpoint can help to express some solutions more naturally.
%h4#kernel-threads Kernel threads
%p
The real solution, where the kernel knows about threads and does the scheduling, took some while to become standard and
makes processes more complicated a fair degree. Luckily we dont code kernels and dont have to worry.
%p
But we do have to deal with the issues that come up. The isse is off course data corruption. I dont even want to go into
how to fix this, or the different ways that have been introduced, because the main thrust becomes clear in the next chapter:
%h3#broken-model Broken model
%p
My main point about threads is that they are one of the worse hacks, especially in a c environemnt. Processes had a good
model of a programm with a global memory. The equivalent of threads would have been shared memory with
%strong many
programs
connected. A nightmare. It even breaks that old turing idea and so it is very difficult to reason about what goes on in a
multi threaded program, and the only ways this is achieved is by developing a more restrictive model.
%p
In essence the thread memory model is broken. Ideally i would not like to implement it, or if implemented, at least fix it
first.
%p But what is the fix? It is in essence what the process model was, ie each thread has its own memory.
%h3#thread-memory Thread memory
%p
In OO it is possible to fix the thread model, just because we have no global memory access. In effect the memory model
must be inverted: instead of almost all memory being shared by all threads and each thread having a small thread local
storage, threads must have mostly thread specific data and a small amount of shared resources.
%p
A thread would thus work as a process used. In essence it can update any data it sees without restrictions. It must
exchange data with other threads through specified global objects, that take the role of what ipc used to be.
%p In an oo system this can be enforced by strict pass-by-value over thread borders.
%p
The itc (inter thread communication) objects are the only ones that need current thread synchronization techniques.
The one mechanism that could cover all needs could be a simple lists.
%h3#rubyx RubyX
%p
The original problem of what a program does during a kernel call could be solved by a very small number of kernel threads.
Any kernel call would be listed and “c” threads would pick them up to execute them and return the result.
%p
All other threads could be managed as green threads. Threads may not share objects, other than a small number of system
provided.