January 22, 2007
Kevin Bealer wrote:
>    I could probably fix this if I knew how to use static if or "is" to find
>    out whether something is a static array or not.

Phobos has an isStaticArray template in std.traits.

Nice work btw.
January 23, 2007
== Quote from Mikola Lysenko (mclysenk@mtu.edu)'s article
> Saaa wrote:
> >
> > As I read it, futurism takes care of threading and synchronization of data
> > (between threads).
> >
> >
>
> Futures are a powerful technique to increase the parallelism of an application without much extra programming effort.  But as much as I like futures, it is Pollyannaish to claim that they solve all thread related problems.  Proper use of futures prevents the future threads from communicating altogether eliminating concurrency.  This is both good and bad; on the one hand concurrent programming is always risky business, yet in most situations involving threads concurrency is unavoidable.  A classic example is the dining philosophers problem. Each philosopher must communicate in order to share common resources.

I see some truth in this.  I also think the dining philosopher problem would be less difficult if you have a controlling algorithm that can arbitrate between the three philosophers -- in other words, the problem is difficult because it is formulated as a peer-to-peer design.

I think there are tasks that futures don't solve.  For example, if you want a status report written to a disk file every N seconds, or you need to control access to a large central hash table, then futures are not the normal solution to those kinds of problems.

But I don't see a conflict - I think you can have a status report thread, a mutex-serialized hash table, and still use futures to multithread the complex work loads.

There are different uses for multithreaded designs.  One of these is to break up complex work, and I think this is where mutexes are awkward and futures are elegant.

Kevin Bealer

January 23, 2007
== Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> Kevin Bealer wrote:
...
> I tried assigning directly, and it didn't like that, hence the foreach loop.  Since it's working on template arguments, it *should* unroll at compile time.

I went through the same thing - but probably quicker because I could look at your example.  It's not a problem; maybe its just a temporary limitation.

> > 2. You can't pass a char[10] (for example) to a function that uses a char[]
> >    input -- instead, you need to ask for the slice, for example, using
> >    syntax like:
> >
> >      make_future(& work, 1234, "some string"[]);
> >                                             ^^
> >
> >    I could probably fix this if I knew how to use static if or "is" to find
> >    out whether something is a static array or not.  For now, the above syntax
> >    is a not-too-painful workaround.
> >
> > Kevin
>
> Ah yes, I've had a few problems with this myself (in other things). The trick here is that instead of specialising on the type of the function, I'm specialising on the type of the arguments supplied.  In most cases, this isn't a problem, but it does cause problems for arrays (and possibly a few other cases).
>
> (I think that) what you need to do is to check to see if an argument is an array, and then check the corresponding type in the function call, and make any necessary casts/conversions.

I'll look into this angle.  I imagine it will require some type-list style tuple recursion.

> Of course, the alternative is to move the function out to the template argument as an alias, and THEN specialise on the argument type of that (which I've done in my OGL/SDL safety templates), which would make it look like this:
>
>      make_future!(work)(1234, "some string");
>
> At least, I think it would :P

I'm going to try to get the other one smoothed out - it looks a little cleaner to me right now.

> This is one of the reasons I love D: programming in it is like opening up a shiny russian doll: every time you think you know what's going on, there's a whole 'nother layer to play with.

"Learning is fun!" -- Bender

> Oh hang on, that was supposed to be the "peeling an onion" metaphor, wasn't it?  Oh well...
>
> 	-- Daniel "No, ogres are NOT like cake!"

Thanks,
Kevin
January 23, 2007
== Quote from Lutger (lutger.blijdestijn@gmail.com)'s article
> Kevin Bealer wrote:
> >    I could probably fix this if I knew how to use static if or "is" to find
> >    out whether something is a static array or not.
> Phobos has an isStaticArray template in std.traits.
> Nice work btw.

Thanks.  I didn't end up using the static array test but I managed to get a solution using other tools from that module -- thanks for reminding me of it.

Kevin
January 23, 2007
== Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> Kevin Bealer wrote:
...
>
> > 2. You can't pass a char[10] (for example) to a function that uses a char[]
> >    input -- instead, you need to ask for the slice, for example, using
> >    syntax like:
> >
> >      make_future(& work, 1234, "some string"[]);
> >                                             ^^
> >
> >    I could probably fix this if I knew how to use static if or "is" to find
> >    out whether something is a static array or not.  For now, the above syntax
> >    is a not-too-painful workaround.
> >
> > Kevin
>
> Of course, the alternative is to move the function out to the template argument as an alias, and THEN specialise on the argument type of that (which I've done in my OGL/SDL safety templates), which would make it look like this:
>
>      make_future!(work)(1234, "some string");
>
> At least, I think it would :P
>
> This is one of the reasons I love D: programming in it is like opening up a shiny russian doll: every time you think you know what's going on, there's a whole 'nother layer to play with.

I didn't fully appreciate this statement until I got the solution working. Now my head hurt like back when I was learning recursion for the first time, which is a sign that my mental software is getting a refactoring. ;)

Template metaprogramming requires another layer of meta-thinking.  Practice helps too (with the syntax especially).  I think I'm finally starting to "absorb" what tuples are doing and why variadic stuff works the way it does.

The requirement for "[]" is gone now, but more fundamentally, conversions between arguments and parameters should happen in the 'right direction'.

This is accomplished by extracting the parameters from the delegate inside of make_future() (using a helper template).  The changes are in SVN but for those of you just reading along, these are the modified lines:

template FutureTypeGroup(Delegate, Args...) {
    alias ReturnType!(Delegate)          Return;
    alias ParameterTypeTuple!(Delegate)  Params;
    alias Future!(Return, Params)        TFuture;
}

FutureTypeGroup!(Delegate, Args).TFuture
make_future(Delegate, Args...)(Delegate cmd, Args args)
{
    return new FutureTypeGroup!(Delegate, Args).TFuture(cmd, args);
}

I think the fact that this change affects so few lines is a testament to the increasing expressiveness of D.

Kevin
January 23, 2007
On Mon, 22 Jan 2007 03:19:53 +0200, Kevin Bealer <kevinbealer@gmail.com> wrote:
[snip]
> I would also recommend using more thread pool threads that hardware threads,
> because some operations (like reading files) are IO bound, and the system benefits
> if it can switch to a CPU bound thread.  So you always want at least the number of
> hardware threads, but I think its a matter of tuning how many more than that.  If
> you think each task is about 50% CPU bound, you might want something like 2x the
> number of hardware threads.

Nice job! I'm looking forward to use futures in the future. :)

BTW, is there a way to know the number of hardware threads (and maybe the number of CPUs)?

IMHO it would be good to have something like this too:

  new ThreadPool(4);    //pool of 4 threads
  new ThreadPool();     //pool of X threads, where X is the # of hardware threads
  new ThreadPool(1.5);  //pool of 1.5 * X threads (>= 1, rounded up perhaps)

(I think one should, in general, always use thread counts relative to the number of hardware threads.)
January 23, 2007
Kristian Kilpi kirjoitti:
> BTW, is there a way to know the number of hardware threads (and maybe
> the number of CPUs)?

std.cpuid has threadsPerCPU(). See
http://www.digitalmars.com/d/phobos/std_cpuid.html.

How does one know how many hw threads there are available, if two multi-threaded programs are running?
January 23, 2007
Kevin Bealer wrote:
> == Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> 
>>Of course, the alternative is to move the function out to the template
>>argument as an alias, and THEN specialise on the argument type of that
>>(which I've done in my OGL/SDL safety templates), which would make it
>>look like this:
>>
>>     make_future!(work)(1234, "some string");
>>
>>At least, I think it would :P
>>
>>This is one of the reasons I love D: programming in it is like opening
>>up a shiny russian doll: every time you think you know what's going on,
>>there's a whole 'nother layer to play with.
> 
> 
> I didn't fully appreciate this statement until I got the solution working.
> Now my head hurt like back when I was learning recursion for the first time,
> which is a sign that my mental software is getting a refactoring. ;)
> 
> Template metaprogramming requires another layer of meta-thinking.  Practice
> helps too (with the syntax especially).  I think I'm finally starting to
> "absorb" what tuples are doing and why variadic stuff works the way it does.

I know the feeling.  There's nothing quite like the moment when something new finally clicks into place.

> The requirement for "[]" is gone now, but more fundamentally, conversions
> between arguments and parameters should happen in the 'right direction'.
> 
> This is accomplished by extracting the parameters from the delegate inside
> of make_future() (using a helper template).  The changes are in SVN but for
> those of you just reading along, these are the modified lines:
> 
> template FutureTypeGroup(Delegate, Args...) {
>     alias ReturnType!(Delegate)          Return;
>     alias ParameterTypeTuple!(Delegate)  Params;
>     alias Future!(Return, Params)        TFuture;
> }
> 
> FutureTypeGroup!(Delegate, Args).TFuture
> make_future(Delegate, Args...)(Delegate cmd, Args args)
> {
>     return new FutureTypeGroup!(Delegate, Args).TFuture(cmd, args);
> }

Now, see, I hadn't thought of trying that: using another level of indirection to coax the compiler into handling the conversions itself. Very nice.

> I think the fact that this change affects so few lines is a testament
> to the increasing expressiveness of D.
> 
> Kevin

That, and our ability to abuse^Hmake use of some of D's advanced features :)

	-- Daniel
January 24, 2007
== Quote from Jari-Matti_Mäkelä (jmjmak@utu.fi.invalid)'s article
> Kristian Kilpi kirjoitti:
> > BTW, is there a way to know the number of hardware threads (and maybe
> > the number of CPUs)?
>
> std.cpuid has threadsPerCPU(). See
> http://www.digitalmars.com/d/phobos/std_cpuid.html.
>
> How does one know how many hw threads there are available, if two multi-threaded programs are running?

My tendency would be for each program to use enough threads to utilize all the hardware threads.  This means that there will be more threads running than CPUs exist, but I don't see extra threads as especially harmful.  This is even more true if one or the other application is sometimes idle.

For example, if a multithreaded web browser is running, it might use all the CPUs when the user surfing, but after each page loads, the threads will all stop while the user reads the page.

I think this is the case for most programs on a desktop machine.  For server farms, more tuning is required, and most workloads are already pretty well optimized for the specific hardware.

(But see also the latest changes to futurism.)

Kevin
January 24, 2007
== Quote from Kristian Kilpi (kjkilpi@gmail.com)'s article
> On Mon, 22 Jan 2007 03:19:53 +0200, Kevin Bealer <kevinbealer@gmail.com>=
...
> Nice job! I'm looking forward to use futures in the future. :)
> BTW, is there a way to know the number of hardware threads (and maybe the
> number of CPUs)?
>
> IMHO it would be good to have something like this too:
>    new ThreadPool(4);    //pool of 4 threads
>    new ThreadPool();     //pool of X threads, where X is the # of hardwa=
> re  =
> threads
>    new ThreadPool(1.5);  //pool of 1.5 * X threads (>=3D 1, rounded up  =
> perhaps)
> (I think one should, in general, always use thread counts relative to th=
> e  =
> number of hardware threads.)

I added something like this to Futurism; it uses five criteria to pick the number of threads to use, in this order:

1. Value passed to constructor (if any).
2. Number from envar FUTURISM_DEFAULT_THREADS (if any).
3. On linux, use #cores * overlap from /proc/cpuinfo.
4. On other x86, use std.cpuid.threadsPerCore * overlap.
5. If all else fails, assume 2 cpus * overlap.

Here, overlap is defined as 4, so typically you will see
4 threads on a single CPU machine or 8 on a dual core.

The envar is there so that people with several multithreaded apps can balance their own system load if they like.  All numbers except for #1 will actually use N-1 worker threads, because the library is designed so that the main thread pulls some of the weight.

Kevin