User talk:Philosopher/Nasal introspection: Difference between revisions

Jump to navigation Jump to search
m
→‎Debugging Threads: something for the Nasal console
m (→‎Debugging Threads: something for the Nasal console)
Line 168: Line 168:


</syntaxhighlight>
</syntaxhighlight>
Manual locking/synchronization is tedious, fragile and often error-prone, I still have to check what exactly your code is doing, wrapping everything in a single object is definitely helpful, for example run this snippet of code in your Nasal console and see for yourself:
<syntaxhighlight lang="php">
var DBG_LOCKS = 1;
##
# a simple Lockable class that wraps lock/unlock functionality
#
var Lockable = {};
Lockable.new = func(name) {
  var l = thread.newlock();
  return { parents:[Lockable],LOCK:l,name:name };
}
Lockable.lock = func thread.lock(me.LOCK);
Lockable.unlock = func thread.unlock(me.LOCK);
##
# also print some stats to the console
#
Lockable.do_atomic = func(what, location="(none)" ) {
DBG_LOCKS and print(location,":Trying to lock:", me.name);
var start=systime();
me.lock();
DBG_LOCKS and print(location,":Lock held:", me.name);
DBG_LOCKS and print(location,":Waiting time:", systime() - start);
what();
me.unlock();
DBG_LOCKS and print(location,":Lock released:", me.name);
}
##
# extend the Lockable class with counter functionality
var counter = Lockable.new("counter");
counter.value = 0;
counter.get = func me.value;
counter.inc = func(meta) me.do_atomic( func me.value +=1, meta );
counter.dec = func(meta) me.do_atomic( func me.value -=1, meta );
##
# we'll run this as the "task" in our worker threads
var stress_test = func(thread_id) {
for (var i=0;i < 50; i+=1) {
counter.inc( thread_id );
counter.dec( thread_id );
}
}
# start a bunch of threads, each incrementing/decrementing the counter concurrently (depending on your hardware and OS)
for(var i=0;i < 180; i+=1) {
  func {
  var c=i;
  thread.newthread(func stress_test("Thread #" ~ c));
}()
}
</syntaxhighlight>
Similarly, a wrapper to abstract threading and thread pools would make things more intuitive - for example, we don't need to use a "busy-wait" loop for all threads to complete, you can simply use semaphores here, but also to send a worker thread sleeping when no work is available, in general it is more efficient to reuse previously allocated threads than spawn/terminate and re-create new ones all the time.


== GC Stats ==
== GC Stats ==
At the very least, we should also dump stats for each naPool - so that we know how objects are distributed (scalars, vectors, hashes, ghosts). Afterwards, we should look into exposing a function that dumps stats per namespace, so that we can use an extension function that dumps stats gathered via mark() - i.e. to tell how tell how heavy a certain namespace is.
At the very least, we should also dump stats for each naPool - so that we know how objects are distributed (scalars, vectors, hashes, ghosts). Afterwards, we should look into exposing a function that dumps stats per namespace, so that we can use an extension function that dumps stats gathered via mark() - i.e. to tell how tell how heavy a certain namespace is.

Navigation menu