Jump to page: 1 2 3
Thread overview
Futurism lib (futures in D)
Jan 21, 2007
Kevin Bealer
Jan 21, 2007
Kevin Bealer
Jan 21, 2007
Saaa
Jan 21, 2007
Saaa
Jan 22, 2007
Kevin Bealer
Jan 22, 2007
Mikola Lysenko
Jan 23, 2007
Kevin Bealer
Jan 21, 2007
Daniel Keep
Jan 22, 2007
Kevin Bealer
Jan 23, 2007
Kristian Kilpi
Jan 24, 2007
Kevin Bealer
Jan 24, 2007
Kevin Bealer
Jan 27, 2007
Walter Bright
Jan 27, 2007
Frits van Bommel
Jan 27, 2007
Walter Bright
Jan 22, 2007
Kevin Bealer
Jan 22, 2007
Daniel Keep
Jan 23, 2007
Kevin Bealer
Jan 23, 2007
Kevin Bealer
Jan 23, 2007
Daniel Keep
Jan 22, 2007
Lutger
Jan 23, 2007
Kevin Bealer
January 21, 2007
I posted my future implementation dsource.org.  It's called "Futurism".  Here is a simple example of using it:

int main()
{
    // Create a pool of 5 threads
    ThreadPool P = new ThreadPool(5);
    scope(exit) P.stop;

    alias Future!(char[]) FVec;

    char[] a = args[1], b = args[2], c = args[3];

    // Starting reading two files at once:
    FVec f1 = new FVec({ return cast(char[]) read(a); });
    FVec f2 = new FVec({ return cast(char[]) read(b); });



    int total_length = f1.value.length + f2.value.length;

    writefln("total length is %s", f1.value.length + );

    return 0;
}

January 21, 2007
Sorry - the last post got posted early due to an errant keystroke.  The code should look like this:

> int main()
> {
>     // Create a pool of 5 threads
>     ThreadPool P = new ThreadPool(5);
>     scope(exit) P.stop;
>
>     alias Future!(char[]) FVec;
>     char[] a = args[1], b = args[2], c = args[3];
>
>     // Starting reading two files at once:
>     FVec f1 = new FVec({ return cast(char[]) read(a); });
>     FVec f2 = new FVec({ return cast(char[]) read(b); });
>
>     // You can do other stuff here if you like
>     writefln("files are being read");
>
>     // This line will wait for both files to be read.
>     int total_length = f1.value.length + f2.value.length;
>     writefln("total length is %s", f1.value.length + );
>
>     return 0;
> }

It took me a bit longer than I expected, when I accidentally did this:

dmd -of~/somefile ...

And ended up with directory called "~".  Naturally I did "rm -rvf ~".

There wasn't much else in my home directory right now (new computer), but I was able to recover the source for this stuff using "strings < /dev/hda5" and some elaborate "grep" commands.

Try it out if you like, let me know if it has bugs or missing features.

Thanks,
Kevin
January 21, 2007
Kevin Bealer wrote:
> I posted my future implementation dsource.org.  It's called "Futurism".  Here
> is a simple example of using it:
> 
> int main()
> {
>     // Create a pool of 5 threads
>     ThreadPool P = new ThreadPool(5);
>     scope(exit) P.stop;
> 
>     alias Future!(char[]) FVec;
> 
>     char[] a = args[1], b = args[2], c = args[3];
> 
>     // Starting reading two files at once:
>     FVec f1 = new FVec({ return cast(char[]) read(a); });
>     FVec f2 = new FVec({ return cast(char[]) read(b); });
> 
> 
> 
>     int total_length = f1.value.length + f2.value.length;
> 
>     writefln("total length is %s", f1.value.length + );
> 
>     return 0;
> }
> 

Looks good; more sophisticated than my own.  A few initial thoughts:

1) Have you considered stealing my code and using the "future(...)" syntax I used?  It seems a bit cleaner than having to alias the class, and then instantiating objects.

2) You say that ThreadPools must be stopped before being deleted.  Maybe it would be good to have a ThreadPoolScope class that must be scoped, and will call stop for you.

3) Speaking of the thread pools, have you considered creating a default pool with (number of hardware threads) threads?  I think std.cpuid can give you those numbers, but I don't know enough to say whether it would a good idea performance-wise.

Again, it looks really good.  Might have to start using it myself :)

	-- Daniel
January 21, 2007
I read wikipedia about future and it sounds interesting.
Do you have a bit more information, or some place where I can get it?

As I read it, futurism takes care of threading and synchronization of data (between threads).


January 21, 2007
:) http://www.dsource.org/projects/futurism/browser/trunk/futurism/readme.txt



January 22, 2007
== Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> Kevin Bealer wrote:
...
> Looks good; more sophisticated than my own.  A few initial thoughts:
>
> 1) Have you considered stealing my code and using the "future(...)" syntax I used?  It seems a bit cleaner than having to alias the class, and then instantiating objects.

I wanted the first checkin to be "stealing free" ;), but I was actually going to ask if you minded me adapting the tuples interface from yours.  I guess not, so I'll look at doing that then.

> 2) You say that ThreadPools must be stopped before being deleted.  Maybe it would be good to have a ThreadPoolScope class that must be scoped, and will call stop for you.

Couldn't hurt.

> 3) Speaking of the thread pools, have you considered creating a default pool with (number of hardware threads) threads?  I think std.cpuid can give you those numbers, but I don't know enough to say whether it would a good idea performance-wise.

I'm not sure about the design.  I originally had thread pools call stop() from its own destructor, but of course that doesn't work because the Thread object can be garbage collected first.

What I'm not sure of is this: all objects are garbage collected at program
termination, but static destructors (static ~this()) blocks are also called at
program termination.  Is there a guarantee that the GC runs after the ~this() blocks?

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.

> Again, it looks really good.  Might have to start using it myself :)
> 	-- Daniel

Thanks - I appreciate the suggestions, too.

Kevin
January 22, 2007
== Quote from Saaa (empty@needmail.com)'s article
> I read wikipedia about future and it sounds interesting.
> Do you have a bit more information, or some place where I can get it?
> As I read it, futurism takes care of threading and synchronization of data
> (between threads).

Let's say I needed to find the sum of an array.

int sum(int[] x)
{
    int rv = 0;
    foreach(int y; x) {
        rv += y;
    }
    return rv;
}

On a multicpu machine, this operation will run on one CPU.  With futures, I can write another routine that utilizes up to 4 CPUs:

int sum_4(int[] x)
{
    alias Future!(int) FInt;
    int part = x.length/4;

    // Each of these queues a work item to be done either on some
    // other thread, or by the main thread here.

    FInt x1 = new FInt({ return sum(x[0..part]); });
    FInt x2 = new FInt({ return sum[part..part*2]); });
    FInt x3 = new FInt({ return sum[part*2..part*3]); });
    FInt x4 = new FInt({ return sum[part*3..$]; });

    // The addition below waits for each results to complete
    // before adding it to the total.

    return x1.value + x2.value + x3.value + x4.value;
}

In this case, the four futures will split the array into four parts and queue each quarter as a work item.  The four parts will be done in parallel.  It should take about 1/4 as long, minus some overhead for building the future objects and so on.

The idea is that any time a function needs to do several things that don't depend
on each other, they can be represented as futures and the program will
run in parallel.

My motivation for writing this was a talk by Herb Sutter, its available on Google video here:

http://video.google.com/videoplay?docid=7625918717318948700&q=sutter

It's pretty long, but interesting I thought -- his basic argument is that computers have gotten faster up till now mostly because the CPU is higher megahertz with better pipelining.  But from now on, computers will run faster mostly because they have a lot of CPUs or cores working together.

We are seeing the first of this with the dual core and quad core CPUs they are producing now.

But the *software* won't actually run any faster unless the programs can be written in a multithreaded way.  You will have the same "Photoshop" running at the same speed on one CPU, with other CPUs idling away doing nothing.

The problem is that it's hard to write good applications that take advantage of the extra CPUs.  It's very hard to write good code with just Thread and Mutex semantics, and monitor/sleep is only a little better.

Worse, if I write a library that uses mutexes to synchronize access, I can't really combine it with another library that does the same thing, because to design mutex-using code that doesn't deadlock, you need to know everything about the locking patterns for the whole application.  If you combine two libraries that can't deadlock, the result often *will* have potential deadlocks.

But, with the future idiom, you can write code that uses extra threads to do work whenever you have two calculations that don't depend on each other.  In the example above, the sum of four arrays can be computed seperately.  In the Futurism source code on dsource, there is a qsort algorithm that uses futures to do work in parallel.

Kevin
January 22, 2007
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.
January 22, 2007
== Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> Kevin Bealer wrote:
...
> > int main()
> > {
> >     // Create a pool of 5 threads
> >     ThreadPool P = new ThreadPool(5);
> >     scope(exit) P.stop;
> >
> >     alias Future!(char[]) FVec;
> >
> >     char[] a = args[1], b = args[2], c = args[3];
> >
> >     // Starting reading two files at once:
> >     FVec f1 = new FVec({ return cast(char[]) read(a); });
> >     FVec f2 = new FVec({ return cast(char[]) read(b); });
> >
> >
> >
> >     int total_length = f1.value.length + f2.value.length;
> >
> >     writefln("total length is %s", f1.value.length + );
> >
> >     return 0;
> > }
> >
> Looks good; more sophisticated than my own.  A few initial thoughts:
>
> 1) Have you considered stealing my code and using the "future(...)" syntax I used?  It seems a bit cleaner than having to alias the class, and then instantiating objects.

By the way, I've done this part -- I wrote a function called make_future, (can't use "future" since thats the module name) and modified the templates in future.d to store the argument tuples.

There is still things I'm not sure about:

1. Why is a foreach() loop needed to copy a tuple value to another tuple of
   the same type?

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
January 22, 2007
Kevin Bealer wrote:
> == Quote from Daniel Keep (daniel.keep+lists@gmail.com)'s article
> 
>>Kevin Bealer wrote:
>>>Daniel Keep wrote a whole buncha junk, *yoink!*
>>
>>Looks good; more sophisticated than my own.  A few initial thoughts:
>>
>>1) Have you considered stealing my code and using the "future(...)"
>>syntax I used?  It seems a bit cleaner than having to alias the class,
>>and then instantiating objects.

P.S.  Of course I don't mind; I'm happy to be able to contribute!

> By the way, I've done this part -- I wrote a function called make_future,
> (can't use "future" since thats the module name) and modified the templates
> in future.d to store the argument tuples.
> 
> There is still things I'm not sure about:
> 
> 1. Why is a foreach() loop needed to copy a tuple value to another tuple of
>    the same type?

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.

> 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.

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.

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!"
« First   ‹ Prev
1 2 3