C# 3.0 adding functional suppoty and Multi core processors

S

shumaker

I just read an overview of C# 3.0, and it occured to me that some of
these features could make it much easier to write programs that
automatically make use of multi core processors. After reading that
lambda functions are available in 3.0, I read up on the advantages of
functional programming, and one of the advantages is that tasks
performed in a functional language do not have side effects, and thus
tasks can be divided between processors easily without dealing with
concurrency as much.

What I'm wondering, if someone exprienced with functional programming
can chime in, are the added features enough to program tasks in a
purely functional manner?

I'm imagining some sort of architecture where each function call in a
task is automatically handed off to a different worker thread. As long
as we write the task in a functional manner, where there is no
dependancy on side effects, then we don't have to worry about the
locking and synchornization issues.

It's been a long time since I wrote anything in a functional
programming language, and am not deeply familier with C# 3.0, but I
wanted to post this and see what other's opinions are.
 
A

Andrew Faust

I'm imagining some sort of architecture where each function call in a
task is automatically handed off to a different worker thread. As long
as we write the task in a functional manner, where there is no
dependancy on side effects, then we don't have to worry about the
locking and synchornization issues.

Simply calling each function call in a seperate worker thread won't
automatically give you benefits of a multi-core or processor system as
the calling function will need to block until the callee returns. Thus
if you're using thirty different threads but 29 of them are blocking you
really don't get any advantages.

Besides, IIRC from college, functional languages are really good at
solving certain problem sets, but for other problem sets they are
actually more complex to use.

Andrew Faust
 
B

Barry Kelly

Andrew Faust said:
Simply calling each function call in a seperate worker thread won't
automatically give you benefits of a multi-core or processor system as
the calling function will need to block until the callee returns.

Separating each function call into a separate thread pretty much implies
*not* blocking on the function result immediately, otherwise there'd be
no point in creating a separate thread at all. Therefore the most
sensible route is to call multiple functions one after the other, then
wait on them to return only when the return value is needed. The fact
that it's a functional approach implies that the return value is the
only side-effect anyway. Imagine code something like this:

---8<---
Active<int> first = CalcFirst();
Active<int> second = CalcSecond();
return new Active<int>(delegate { return first.Value * second.Value; });
--->8---

.... where Active<T>.Value waits for the calculation to finish, and
Active<T>'s constructor takes a delegate Evaluator that looks like:

delegate T Evaluator<T>()

.... and runs it on a background thread.

That code borrows from the "active" ideas for C++ presented by Herb
Sutter at the last PDC. I've written some ideas like this in my own
libraries. The can work well for limited scenarios - where the
calculation cost is much greater than the current overhead for handing
off to the threadpool.

If the .NET framework had efficient pipelined low-level threading
primitives, one could see this as being an efficient way to use multiple
cores, imagining that CalcFirst, CalcSecond and '*' on ints were
operations sufficiently slow to benefit from parallel calculation, which
implies extra latency for communicating the return values.
Besides, IIRC from college, functional languages are really good at
solving certain problem sets, but for other problem sets they are
actually more complex to use.

If all your data is immutable for a particular subgraph and you need to
perform calculations or copying transformations on this subgraph, a
functional approach can make life much easier and less buggy, as well as
possibly improving performance in a multi-core world. I know its helped
me in the past.

-- Barry
 
A

Atmapuri

Hi!
cores, imagining that CalcFirst, CalcSecond and '*' on ints were
operations sufficiently slow to benefit from parallel calculation, which
implies extra latency for communicating the return values.

Considering that the OS switches between threads about every 20ms.
It would only make sense to use multiple cores for tasks that take
longer than 20ms to compute. And that is usually a very "fat" chunk of
processing.

Regards!
Atmapuri
 
B

Barry Kelly

Atmapuri said:
Hi!


Considering that the OS switches between threads about every 20ms.

This assumes that there are other tasks that are using 100% of every
CPU. Also, a thread's time slice ends sooner if it blocks on a
synchronization object. It only makes sense to multi-thread these
calculations if not all CPUs are being utilized to 100% anyway, which
implies that there will (probably) be an idle CPU for the task, which in
turn implies that it should take less than 20ms before the thread gets
started (a slice "timeout" will not be needed at all if a CPU is idle).
It would only make sense to use multiple cores for tasks that take
longer than 20ms to compute. And that is usually a very "fat" chunk of
processing.

I don't agree with your reasoning.

-- Barry
 
S

shumaker

Ah, I love when I hit tab and then reallize I can't tab in a text box
because it changes focus, then hit backspace and it goes back a page
and I lose everything I have written :(

In a nutshell, if you have a recursive process that uses the recursive
calls as parameters to itself like Do(Do(n+1,term), Do(n+2,term)) then
your calls can be visuallized as a tree, and you would hand off the
right paramter to another thread, but only until you've filled all
processors. At that point there would be no more handing off, and each
thread would work it's part of the call tree using the normal call
stack process.

If a thread finished before the others, then one of the others could
hand off again at the next most convient split point.

Since we don't want to mess with threads if the recursive process isn't
going to get very deep, the programmer could specifiy conditions in an
imperetive manner such as:

DoInit(int n, int term)
{
if( (n-term) >= 100)
{
FlagUsageOfMultithreading();
}

Do(n,term);
}

If you were processing some sort of large set of data, your condition
would be based on the number of items in the data set. Of course it's
not provable when or if something will terminate, but since you're only
concerned with identifying what is "too small" for multithreading, then
for small values or small sets of data, the programmer should be able
to make a good estimate of how many calls will be required. With that
they would estimate what the breaking point is that makes
multithreading worth it.

I suppose there has already been alot of research in these regards in
the realm of distributed computing. It seems that since the future is
going to be single user systems running multi core processors, then
there is going to be a need for easy to use techniques that allow
programmers to get the most out of these processors.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top