* Fun with Tasking
@ 2007-03-28 15:53 mhamel_98
2007-03-28 16:55 ` Adam Beneschan
0 siblings, 1 reply; 10+ messages in thread
From: mhamel_98 @ 2007-03-28 15:53 UTC (permalink / raw)
Below is sample code I made representing something I wanted to do with
a program, of course it doesn't work quite the way I would like.
Hopefully this sample doesn't cause eye (or brain) injury, it is more
symbolic than useful - the procedure "Exec" is, of course, really a
package (rather, several similar packages).
The problem is, how to "transport" a task? As seen in the sample
below, I would like to use just a single address in the Control_Block
type, but it's never that simple, is it? In actuality, how I get this
program in its full incarnation to work is to define the "test_task"s
in the acc_add_test part of the program. I really don't like doing
this, as I mentioned, "exec" is representative of a family of several
different packages. I would like the task definition to be local to
the "exec" pacakage(s), not all kludged together in acc_add_test,
which then requires all sorts of (ideally private) types being moved
out of the local "exec" packages into acc_add_test and then the
Control_Block record now has nearly a dozen different task pointers
stuffed into it, only one of which is going to be used at any given
time.
One last caveat, the Acc_Add_Test program should have *no* visibility
into the "exec" procedure/package. The "exec" package(s), in fact,
"push" their procedures into the main program. So is there something
simple I'm having a brain fart over in missing here, or is a complete
re-architecture required here?
with Text_Io;
with System;
with System.Address_To_Access_Conversions;
procedure Acc_Add_test is
Exec_Count : constant Natural := 10;
type Control_Block is
record
Task_Addr : System.Address;
end record;
type Mode_Type is (preprocess, process);
procedure Exec (Mode : in Mode_Type;
Cntl : in out Control_Block) is
task type Test_Task is
entry Start (Addr : System.Address);
entry Complete;
end Test_Task;
package Convert is new System.Address_To_Access_Conversions
(Test_Task);
use Convert;
task body Test_Task is
Task_Ptr : Convert.Object_Pointer := null;
Count : Natural := 0;
begin
loop
begin
select
accept Start (Addr : System.Address) do
Task_Ptr := Convert.To_Pointer (Addr);
end Start;
or
accept Complete do
Count := Count + 1;
end Complete;
if Count = Exec_Count then
Text_Io.Put_Line ("Completed All Tasks");
abort Task_Ptr.all;
end if;
end select;
end;
end loop;
end Test_Task;
begin
case Mode is
when Preprocess =>
declare
Task_Ptr : Convert.Object_Pointer;
Address : System.Address;
begin
Task_Ptr := new Test_Task;
Address := Convert.To_Address (Task_Ptr);
Task_Ptr.Start (Address);
Cntl.Task_Addr := Address;
end;
when Process =>
declare
Task_Ptr : Convert.Object_Pointer;
begin
Task_Ptr := Convert.To_Pointer (Cntl.Task_Addr);
Task_Ptr.Complete;
end;
end case;
end Exec;
Controls : Control_Block;
begin
Exec (Preprocess, Controls);
for I in 1 .. Exec_Count loop
Exec (Process, Controls);
end loop;
end Acc_Add_Test;
Thanks for any input!
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-28 15:53 Fun with Tasking mhamel_98
@ 2007-03-28 16:55 ` Adam Beneschan
2007-03-28 17:53 ` mhamel_98
0 siblings, 1 reply; 10+ messages in thread
From: Adam Beneschan @ 2007-03-28 16:55 UTC (permalink / raw)
On Mar 28, 8:53 am, mhamel...@yahoo.com wrote:
> Below is sample code I made representing something I wanted to do with
> a program, of course it doesn't work quite the way I would like.
> Hopefully this sample doesn't cause eye (or brain) injury, it is more
> symbolic than useful - the procedure "Exec" is, of course, really a
> package (rather, several similar packages).
>
> The problem is, how to "transport" a task? As seen in the sample
> below, I would like to use just a single address in the Control_Block
> type, but it's never that simple, is it? In actuality, how I get this
> program in its full incarnation to work is to define the "test_task"s
> in the acc_add_test part of the program. I really don't like doing
> this, as I mentioned, "exec" is representative of a family of several
> different packages. I would like the task definition to be local to
> the "exec" pacakage(s), not all kludged together in acc_add_test,
> which then requires all sorts of (ideally private) types being moved
> out of the local "exec" packages into acc_add_test and then the
> Control_Block record now has nearly a dozen different task pointers
> stuffed into it, only one of which is going to be used at any given
> time.
>
> One last caveat, the Acc_Add_Test program should have *no* visibility
> into the "exec" procedure/package. The "exec" package(s), in fact,
> "push" their procedures into the main program. So is there something
> simple I'm having a brain fart over in missing here, or is a complete
> re-architecture required here?
>
> with Text_Io;
> with System;
> with System.Address_To_Access_Conversions;
>
> procedure Acc_Add_test is
>
> Exec_Count : constant Natural := 10;
>
> type Control_Block is
> record
> Task_Addr : System.Address;
> end record;
>
> type Mode_Type is (preprocess, process);
>
> procedure Exec (Mode : in Mode_Type;
> Cntl : in out Control_Block) is
>
> task type Test_Task is
> entry Start (Addr : System.Address);
> entry Complete;
> end Test_Task;
>
> package Convert is new System.Address_To_Access_Conversions
> (Test_Task);
> use Convert;
>
> task body Test_Task is
> Task_Ptr : Convert.Object_Pointer := null;
> Count : Natural := 0;
> begin
> loop
> begin
> select
> accept Start (Addr : System.Address) do
> Task_Ptr := Convert.To_Pointer (Addr);
> end Start;
>
> or
> accept Complete do
> Count := Count + 1;
> end Complete;
> if Count = Exec_Count then
> Text_Io.Put_Line ("Completed All Tasks");
> abort Task_Ptr.all;
> end if;
> end select;
> end;
> end loop;
> end Test_Task;
>
> begin
> case Mode is
> when Preprocess =>
> declare
> Task_Ptr : Convert.Object_Pointer;
> Address : System.Address;
> begin
> Task_Ptr := new Test_Task;
> Address := Convert.To_Address (Task_Ptr);
> Task_Ptr.Start (Address);
> Cntl.Task_Addr := Address;
> end;
>
> when Process =>
> declare
> Task_Ptr : Convert.Object_Pointer;
> begin
> Task_Ptr := Convert.To_Pointer (Cntl.Task_Addr);
> Task_Ptr.Complete;
> end;
> end case;
> end Exec;
>
> Controls : Control_Block;
>
> begin
> Exec (Preprocess, Controls);
> for I in 1 .. Exec_Count loop
> Exec (Process, Controls);
> end loop;
> end Acc_Add_Test;
>
> Thanks for any input!
Your main problem is that your tasks aren't going to live long
enough. When a task is created by an allocator, it depends on the
"master" that declares the access type (or if the access type is a
derived type, which isn't the case here, on the "ultimate ancestor" of
the access type). See RM section 9.3. In this case, the master that
declares the access type (Convert.Object_Pointer) is the Exec
procedure. This means that any task that you create using this access
type depends on Exec, and Exec will not finish until any such tasks
are completed. It looks to me that you're trying to be able to call
Exec multiple times, and have it start a task the first time and then
use the same task the next time Exec is called. This won't work if
the task type is declared inside Exec, because any such tasks will not
be allowed to keep running if Exec isn't running. You'll need to move
the task types (and the access type that points to the task type)
outside Exec.
This is the only thing that makes sense, anyway. If your task type is
declared inside Exec, the task body will have access to Exec's
parameters and local variables; how then could it be possible for the
task body to stay running while Exec isn't?
I'm not real clear on what you're trying to do. Your sample has
"Exec" as a procedure, which makes it a master, but your discussion
talks about "Exec" *packages*---and a package is not a master. If you
wanted an Exec *package* that declares its own task type and its own
procedure to do the processing, but the task type isn't inside the
procedure, then I suspect that you may be able to accomplish what
you're trying to do; and there should be a way to keep certain things
private without having to resort to hokeyness like System.Address or
Address_To_Access_Conversions. But I'm not up to trying to guess what
you're trying to do; I'd recommend that you redo your example a bit to
avoid the task dependence problem and then ask again if you still
aren't sure how to do what you need to do.
-- Adam
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-28 16:55 ` Adam Beneschan
@ 2007-03-28 17:53 ` mhamel_98
2007-03-28 20:06 ` Ed Falis
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: mhamel_98 @ 2007-03-28 17:53 UTC (permalink / raw)
On Mar 28, 12:55 pm, "Adam Beneschan" <a...@irvine.com> wrote:
> On Mar 28, 8:53 am, mhamel...@yahoo.com wrote:
>
>
>
>
>
> > Below is sample code I made representing something I wanted to do with
> > a program, of course it doesn't work quite the way I would like.
> > Hopefully this sample doesn't cause eye (or brain) injury, it is more
> > symbolic than useful - the procedure "Exec" is, of course, really a
> > package (rather, several similar packages).
>
> > The problem is, how to "transport" a task? As seen in the sample
> > below, I would like to use just a single address in the Control_Block
> > type, but it's never that simple, is it? In actuality, how I get this
> > program in its full incarnation to work is to define the "test_task"s
> > in the acc_add_test part of the program. I really don't like doing
> > this, as I mentioned, "exec" is representative of a family of several
> > different packages. I would like the task definition to be local to
> > the "exec" pacakage(s), not all kludged together in acc_add_test,
> > which then requires all sorts of (ideally private) types being moved
> > out of the local "exec" packages into acc_add_test and then the
> > Control_Block record now has nearly a dozen different task pointers
> > stuffed into it, only one of which is going to be used at any given
> > time.
>
> > One last caveat, the Acc_Add_Test program should have *no* visibility
> > into the "exec" procedure/package. The "exec" package(s), in fact,
> > "push" their procedures into the main program. So is there something
> > simple I'm having a brain fart over in missing here, or is a complete
> > re-architecture required here?
>
> > with Text_Io;
> > with System;
> > with System.Address_To_Access_Conversions;
>
> > procedure Acc_Add_test is
>
> > Exec_Count : constant Natural := 10;
>
> > type Control_Block is
> > record
> > Task_Addr : System.Address;
> > end record;
>
> > type Mode_Type is (preprocess, process);
>
> > procedure Exec (Mode : in Mode_Type;
> > Cntl : in out Control_Block) is
>
> > task type Test_Task is
> > entry Start (Addr : System.Address);
> > entry Complete;
> > end Test_Task;
>
> > package Convert is new System.Address_To_Access_Conversions
> > (Test_Task);
> > use Convert;
>
> > task body Test_Task is
> > Task_Ptr : Convert.Object_Pointer := null;
> > Count : Natural := 0;
> > begin
> > loop
> > begin
> > select
> > accept Start (Addr : System.Address) do
> > Task_Ptr := Convert.To_Pointer (Addr);
> > end Start;
>
> > or
> > accept Complete do
> > Count := Count + 1;
> > end Complete;
> > if Count = Exec_Count then
> > Text_Io.Put_Line ("Completed All Tasks");
> > abort Task_Ptr.all;
> > end if;
> > end select;
> > end;
> > end loop;
> > end Test_Task;
>
> > begin
> > case Mode is
> > when Preprocess =>
> > declare
> > Task_Ptr : Convert.Object_Pointer;
> > Address : System.Address;
> > begin
> > Task_Ptr := new Test_Task;
> > Address := Convert.To_Address (Task_Ptr);
> > Task_Ptr.Start (Address);
> > Cntl.Task_Addr := Address;
> > end;
>
> > when Process =>
> > declare
> > Task_Ptr : Convert.Object_Pointer;
> > begin
> > Task_Ptr := Convert.To_Pointer (Cntl.Task_Addr);
> > Task_Ptr.Complete;
> > end;
> > end case;
> > end Exec;
>
> > Controls : Control_Block;
>
> > begin
> > Exec (Preprocess, Controls);
> > for I in 1 .. Exec_Count loop
> > Exec (Process, Controls);
> > end loop;
> > end Acc_Add_Test;
>
> > Thanks for any input!
>
> Your main problem is that your tasks aren't going to live long
> enough. When a task is created by an allocator, it depends on the
> "master" that declares the access type (or if the access type is a
> derived type, which isn't the case here, on the "ultimate ancestor" of
> the access type). See RM section 9.3. In this case, the master that
> declares the access type (Convert.Object_Pointer) is the Exec
> procedure. This means that any task that you create using this access
> type depends on Exec, and Exec will not finish until any such tasks
> are completed. It looks to me that you're trying to be able to call
> Exec multiple times, and have it start a task the first time and then
> use the same task the next time Exec is called. This won't work if
> the task type is declared inside Exec, because any such tasks will not
> be allowed to keep running if Exec isn't running. You'll need to move
> the task types (and the access type that points to the task type)
> outside Exec.
>
> This is the only thing that makes sense, anyway. If your task type is
> declared inside Exec, the task body will have access to Exec's
> parameters and local variables; how then could it be possible for the
> task body to stay running while Exec isn't?
>
> I'm not real clear on what you're trying to do. Your sample has
> "Exec" as a procedure, which makes it a master, but your discussion
> talks about "Exec" *packages*---and a package is not a master. If you
> wanted an Exec *package* that declares its own task type and its own
> procedure to do the processing, but the task type isn't inside the
> procedure, then I suspect that you may be able to accomplish what
> you're trying to do; and there should be a way to keep certain things
> private without having to resort to hokeyness like System.Address or
> Address_To_Access_Conversions. But I'm not up to trying to guess what
> you're trying to do; I'd recommend that you redo your example a bit to
> avoid the task dependence problem and then ask again if you still
> aren't sure how to do what you need to do.
>
> -- Adam- Hide quoted text -
>
> - Show quoted text -
Thank you Adam. While making this sample program a sneaking suspicion
started to creep up that I really didn't know what I was doing, and
you helped verify that suspicion. What's really going on is I have a
number of tasks I'm going to run, and while themselves completely
independent of one another, they send updates to a single listening,
or collating, task. That is the task at the heart of my initial
post. My approach was to start up this collating task and give it's
pointer (or address) to the processing tasks. This works fine if the
collating task definition is in the acc_add_test procedure and the
Control_Block has a pointer to that task within it. However, I would
like the collating task definition to reside with the "exec" procedure/
package as the sample has it. How would one declare and start up that
task, without any visibility into the package, just a procedure
pointer with a mode argument? My (terribly flawed) idea was the
PreProcess element of the procedure would set that up, but as you
pointed out, the procedure would never exit while the task is
running. So how to define the task and put it on "hold" until the
processing tasks can utilize it? I don't expect it's possible without
incurring as much ugliness as the program has already, but I thought
I'd give it a try. Thanks again =)
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-28 17:53 ` mhamel_98
@ 2007-03-28 20:06 ` Ed Falis
2007-03-28 22:17 ` Randy Brukardt
2007-03-29 4:59 ` Jeffrey R. Carter
2 siblings, 0 replies; 10+ messages in thread
From: Ed Falis @ 2007-03-28 20:06 UTC (permalink / raw)
mhamel_98@yahoo.com wrote:
> Thank you Adam. While making this sample program a sneaking suspicion
> started to creep up that I really didn't know what I was doing, and
> you helped verify that suspicion.
You sound like a fine candidate for a copy of "Concurrency in Ada" by
Burns and Welling. I did one of the first Ada tasking implementations,
and still learned a lot from this book when I read it a couple of years
ago.
- Ed
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-28 17:53 ` mhamel_98
2007-03-28 20:06 ` Ed Falis
@ 2007-03-28 22:17 ` Randy Brukardt
2007-03-29 4:59 ` Jeffrey R. Carter
2 siblings, 0 replies; 10+ messages in thread
From: Randy Brukardt @ 2007-03-28 22:17 UTC (permalink / raw)
<mhamel_98@yahoo.com> wrote in message
news:1175104396.231171.125360@e65g2000hsc.googlegroups.com...
...
> Thank you Adam. While making this sample program a sneaking suspicion
> started to creep up that I really didn't know what I was doing, and
> you helped verify that suspicion. What's really going on is I have a
> number of tasks I'm going to run, and while themselves completely
> independent of one another, they send updates to a single listening,
> or collating, task. That is the task at the heart of my initial
> post. My approach was to start up this collating task and give it's
> pointer (or address) to the processing tasks. This works fine if the
> collating task definition is in the acc_add_test procedure and the
> Control_Block has a pointer to that task within it. However, I would
> like the collating task definition to reside with the "exec" procedure/
> package as the sample has it. How would one declare and start up that
> task, without any visibility into the package, just a procedure
> pointer with a mode argument? My (terribly flawed) idea was the
> PreProcess element of the procedure would set that up, but as you
> pointed out, the procedure would never exit while the task is
> running. So how to define the task and put it on "hold" until the
> processing tasks can utilize it? I don't expect it's possible without
> incurring as much ugliness as the program has already, but I thought
> I'd give it a try. Thanks again =)
I'm not going to try to figure out precisely what it is that you are trying
to do (a listening task doesn't require anything special to listen!), but a
couple of thoughts about trying to manage unrelated tasks with another task.
(1) You really don't want to try to manage unrelated anything in Ada (Ada is
strongly typed, after all). If you are using a very new compiler, probably
the best way to relate some tasks that would otherwise be unrelated is to
define them all to be derived from the same task interface. The idea is that
you would put the control operations into the task interface, and then the
manager would use access-to-interface'class to keep track of the tasks and
call operations on them as needed. But you'll need a complete and non-buggy
implementation of the entire Ada language, as amended; so far as I know only
GNAT Pro even claims to support this. So you'd be better off trying to do
this next year...
Note that you have to start each task individually. The usual trick is to
use a constructor function for that purpose.
(2) Otherwise, I'd say that it can't be done reasonably. You could use
Task-Ids to track the status of the tasks, but that won't help starting
them.
(3) Probably the best plan is to forget about having the management task
interoperate directly with the other tasks. Rather, define an abstract
tagged type (or interface, if you prefer) Processor, and have each package
define any instance of that tagged type. It would have the needed management
operations ("Start", "Status", "Stop", etc.), and again you would create an
appropriate instance of each one (probably with the main program), and have
the management task save a list of these as access-to-Processor'class, and
manage them appropriately.
The advantage of this technique is that you can wrap anything you need in
these operations: if you need a conditional entry call, for instance, you
would just implement one of those operations that way. (That's precisely how
task interfaces get implemented anyway; you're just doing some of the work
that the compiler would done.)
Hope this helps.
Randy.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-28 17:53 ` mhamel_98
2007-03-28 20:06 ` Ed Falis
2007-03-28 22:17 ` Randy Brukardt
@ 2007-03-29 4:59 ` Jeffrey R. Carter
2007-03-29 15:17 ` mhamel_98
2 siblings, 1 reply; 10+ messages in thread
From: Jeffrey R. Carter @ 2007-03-29 4:59 UTC (permalink / raw)
mhamel_98@yahoo.com wrote:
>
> Thank you Adam. While making this sample program a sneaking suspicion
> started to creep up that I really didn't know what I was doing, and
> you helped verify that suspicion. What's really going on is I have a
> number of tasks I'm going to run, and while themselves completely
> independent of one another, they send updates to a single listening,
> or collating, task. That is the task at the heart of my initial
> post. My approach was to start up this collating task and give it's
> pointer (or address) to the processing tasks. This works fine if the
> collating task definition is in the acc_add_test procedure and the
> Control_Block has a pointer to that task within it. However, I would
> like the collating task definition to reside with the "exec" procedure/
> package as the sample has it. How would one declare and start up that
> task, without any visibility into the package, just a procedure
> pointer with a mode argument? My (terribly flawed) idea was the
> PreProcess element of the procedure would set that up, but as you
> pointed out, the procedure would never exit while the task is
> running. So how to define the task and put it on "hold" until the
> processing tasks can utilize it? I don't expect it's possible without
> incurring as much ugliness as the program has already, but I thought
> I'd give it a try. Thanks again =)
Tasking /is/ fun. I seem to be unusual in that I don't find it
difficult. I'd like to be able to show off and solve all your problems,
but I still have no idea what you're trying to achieve. I've got a lot
of information about how you're trying to implement it, but no idea what
"it" is. If you can describe the problem, there's probably an elegant
way to implement a solution in Ada.
--
Jeff Carter
"All citizens will be required to change their underwear
every half hour. Underwear will be worn on the outside,
so we can check."
Bananas
29
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-29 4:59 ` Jeffrey R. Carter
@ 2007-03-29 15:17 ` mhamel_98
2007-03-30 4:06 ` Steve
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: mhamel_98 @ 2007-03-29 15:17 UTC (permalink / raw)
On Mar 29, 12:59 am, "Jeffrey R. Carter" <jrcar...@acm.org> wrote:
> mhamel...@yahoo.com wrote:
>
> > Thank you Adam. While making this sample program a sneaking suspicion
> > started to creep up that I really didn't know what I was doing, and
> > you helped verify that suspicion. What's really going on is I have a
> > number of tasks I'm going to run, and while themselves completely
> > independent of one another, they send updates to a single listening,
> > or collating, task. That is the task at the heart of my initial
> > post. My approach was to start up this collating task and give it's
> > pointer (or address) to the processing tasks. This works fine if the
> > collating task definition is in the acc_add_test procedure and the
> > Control_Block has a pointer to that task within it. However, I would
> > like the collating task definition to reside with the "exec" procedure/
> > package as the sample has it. How would one declare and start up that
> > task, without any visibility into the package, just a procedure
> > pointer with a mode argument? My (terribly flawed) idea was the
> > PreProcess element of the procedure would set that up, but as you
> > pointed out, the procedure would never exit while the task is
> > running. So how to define the task and put it on "hold" until the
> > processing tasks can utilize it? I don't expect it's possible without
> > incurring as much ugliness as the program has already, but I thought
> > I'd give it a try. Thanks again =)
>
> Tasking /is/ fun. I seem to be unusual in that I don't find it
> difficult. I'd like to be able to show off and solve all your problems,
> but I still have no idea what you're trying to achieve. I've got a lot
> of information about how you're trying to implement it, but no idea what
> "it" is. If you can describe the problem, there's probably an elegant
> way to implement a solution in Ada.
>
> --
> Jeff Carter
> "All citizens will be required to change their underwear
> every half hour. Underwear will be worn on the outside,
> so we can check."
> Bananas
> 29- Hide quoted text -
>
> - Show quoted text -
Great help and advice all around, thanks much c.l.a!
Anyhoo Jeff, I'll try to explain in more detail what my program is
doing and hopefully you can see where I'm going wrong. The program
has a number of packages, each handling what I call a subsystem, to be
verbose: ephemeris, residual, vector, covariance and a few others
(this sort of equates to the "exec" procedure in my sample). Each of
these systems has a number of procedures of interest, like a
differencing of quantities, a statistical analysis, etc. Now each of
these procedures of each of these subsystems can have up to 60 (and
more as we go on) objects - each one I handle as it's own task. I've
found that throwing 60 threads at the OS (W2K in this case) is, while
better then serially and single-threaded on multi-cpu's, not terribly
efficient. For this application, I've found most procedures operate
best if N+1 threads are running, N being the processors available.
The output is all .csv files. For awhile, to show an ensemble of
data, I would do a lot of cut and pasting between .csv's, one day, I
thought, why not have the program do this? So I implemented these
"collating" tasks that all 60 or so threads would send some
information to for some sort of unified processing. Each procedure of
each subsystem/package is slightly different either in the data types
being handled or the manner in which the data is processed so a
"generic" collating task isn't really feasible.
That's what's happening, now how do I handle it? As the program
starts, I have each subsystem loads its procedures (via procedure
pointers) to a task manager. A command is recieved via CLI and the
task manager then inserts, let's say, 60 copies of a specified
procedure into a list which are doled out to worker threads so that
only N+1 are active at any time. The issue I'm having is, how do you
have a unique collating task created for those 60 processes to send
updates to? As it is now, I have all of these (nearly a dozen) unique
collating tasks defined in the task manager. I would rather have them
defined in the subsystem package to which they really belong. As the
subsystem packages "push" their procedures to the task manager, the
task manager has no visibility into the subsystem. My aim was to
define an area (a System.Address for example) in a control block
record that the task manager would have space for "a task". What this
task was, what it did, where it came from the task manager would not
know nor care, but it would be the owner, or master, in some sense so
the "preprocess" or initialization component could exit. I hope this
makes a little sense.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-29 15:17 ` mhamel_98
@ 2007-03-30 4:06 ` Steve
2007-03-30 5:46 ` Jeffrey R. Carter
2007-03-31 12:08 ` Stephen Leake
2 siblings, 0 replies; 10+ messages in thread
From: Steve @ 2007-03-30 4:06 UTC (permalink / raw)
<mhamel_98@yahoo.com> wrote in message
news:1175181454.290582.90280@b75g2000hsg.googlegroups.com...
[snip]
>
> Great help and advice all around, thanks much c.l.a!
>
> Anyhoo Jeff, I'll try to explain in more detail what my program is
> doing and hopefully you can see where I'm going wrong. The program
> has a number of packages, each handling what I call a subsystem, to be
> verbose: ephemeris, residual, vector, covariance and a few others
> (this sort of equates to the "exec" procedure in my sample). Each of
> these systems has a number of procedures of interest, like a
> differencing of quantities, a statistical analysis, etc. Now each of
> these procedures of each of these subsystems can have up to 60 (and
> more as we go on) objects - each one I handle as it's own task. I've
> found that throwing 60 threads at the OS (W2K in this case) is, while
> better then serially and single-threaded on multi-cpu's, not terribly
> efficient. For this application, I've found most procedures operate
> best if N+1 threads are running, N being the processors available.
> The output is all .csv files. For awhile, to show an ensemble of
> data, I would do a lot of cut and pasting between .csv's, one day, I
> thought, why not have the program do this? So I implemented these
> "collating" tasks that all 60 or so threads would send some
> information to for some sort of unified processing. Each procedure of
> each subsystem/package is slightly different either in the data types
> being handled or the manner in which the data is processed so a
> "generic" collating task isn't really feasible.
>
You are still rather heavily interlacing your implementation with what you
are looking for, but I think I have at least a partial understanding.
Here are some suggestions:
Don't use function pointers.
Don't use System.Address
In the context of your problem description, these are tools that a C
programmer uses when writing a C program in Ada.
Create an abstract base class (tagged type), and subclass for the specific
functionality of the subsystems. Note: Some Ada programmers may get their
knickers in a bind by my use of terms like "class" and "method" since these
are not Ada keywords and they are keywords in other programming languages, I
am not describing syntax, but structure.
For example a package defining a base class might be something like:
------------------------------
package Data_Processing is
type Data_Processing_Type is abstract tagged null record;
type Data_Processing_Acc is access all Data_Processing_Type'class;
procedure Process_Data( Data_Set : Data_Processing_Type ) is abstract;
end Data_Processing;
--------------------------------
Derive a specfic processing type for one of the types of processing that
needs to be done, hiding any data to be processed in the private part of the
package spec:
with Data_Processing;
use Data_Processing;
package Ephemeris_Processing is
type Ephemeris_Processing_Type is new Data_Processing_Type with private;
procedure Process_Data( Data_Set : Ephemeris_Processing_Type );
function Create_Ephemeris_Processor( data_value : Natural ) return
Data_Processing_Acc;
private
type Ephemeris_Processing_Type is new Data_Processing_Type with
record
data_value : Natural;
end record;
type Ephemeris_Processing_Acc is access all Ephemeris_Processing_Type;
end Ephemeris_Processing;
--------------------------------
with Ada.Text_IO;
with Ada.Integer_Text_IO;
package body Ephemeris_Processing is
procedure Process_Data( Data_Set : Ephemeris_Processing_Type ) is
begin
Ada.Text_IO.Put( "Value is:" );
Ada.Integer_Text_IO.Put( Data_Set.data_value );
Ada.Text_IO.New_Line;
end Process_Data;
function Create_Ephemeris_Processor( data_value : Natural ) return
Data_Processing_Acc is
ret : Ephemeris_Processing_Acc;
begin
ret := new Ephemeris_Processing_Type;
ret.data_value := data_value;
return Data_Processing_Acc(ret);
end Create_Ephemeris_Processor;
end Ephemeris_Processing;
--------------------------------
And then use dynamic dispatching to invoke the processing of the data for
the specific processing to be done.
with Data_Processing;
use Data_Processing;
with Ephemeris_Processing;
use Ephemeris_Processing;
procedure Dispatch_Demo is
work : Data_Processing_Acc;
begin
work := Create_Ephemeris_Processor( 42 );
Process_Data( work.ALL );
end Dispatch_Demo;
--------------------------------
You can compile and test the snippets of code above to see the dispatching
work.
This doesn't really describe how to handle the tasking, but a better way to
break down the problem.
Derive different classes from Data_Processing_Type for each of the different
types of processing you need. Information about each of the derived classes
is encapsulated in a separate package for each type of processing.
You can then create list of processing to be done and send elements of the
list to a task. The task receives an access to a data processing type and
calls the Process_Data function.
One possible implementation is to put the list of operations to be done in a
protected object. Create as many threads as you want and have each thread
get a data processing object from the list, process the data, and then go
back for more work to do. This approach scales well.
I hope this helps,
Steve
(The Duck)
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-29 15:17 ` mhamel_98
2007-03-30 4:06 ` Steve
@ 2007-03-30 5:46 ` Jeffrey R. Carter
2007-03-31 12:08 ` Stephen Leake
2 siblings, 0 replies; 10+ messages in thread
From: Jeffrey R. Carter @ 2007-03-30 5:46 UTC (permalink / raw)
mhamel_98@yahoo.com wrote:
>
> Anyhoo Jeff, I'll try to explain in more detail what my program is
> doing and hopefully you can see where I'm going wrong. The program
> has a number of packages, each handling what I call a subsystem, to be
> verbose: ephemeris, residual, vector, covariance and a few others
> (this sort of equates to the "exec" procedure in my sample). Each of
> these systems has a number of procedures of interest, like a
> differencing of quantities, a statistical analysis, etc. Now each of
> these procedures of each of these subsystems can have up to 60 (and
> more as we go on) objects - each one I handle as it's own task. I've
> found that throwing 60 threads at the OS (W2K in this case) is, while
> better then serially and single-threaded on multi-cpu's, not terribly
> efficient. For this application, I've found most procedures operate
> best if N+1 threads are running, N being the processors available.
> The output is all .csv files. For awhile, to show an ensemble of
> data, I would do a lot of cut and pasting between .csv's, one day, I
> thought, why not have the program do this? So I implemented these
> "collating" tasks that all 60 or so threads would send some
> information to for some sort of unified processing. Each procedure of
> each subsystem/package is slightly different either in the data types
> being handled or the manner in which the data is processed so a
> "generic" collating task isn't really feasible.
>
> That's what's happening, now how do I handle it? As the program
> starts, I have each subsystem loads its procedures (via procedure
> pointers) to a task manager. A command is recieved via CLI and the
> task manager then inserts, let's say, 60 copies of a specified
> procedure into a list which are doled out to worker threads so that
> only N+1 are active at any time. The issue I'm having is, how do you
> have a unique collating task created for those 60 processes to send
> updates to? As it is now, I have all of these (nearly a dozen) unique
> collating tasks defined in the task manager. I would rather have them
> defined in the subsystem package to which they really belong. As the
> subsystem packages "push" their procedures to the task manager, the
> task manager has no visibility into the subsystem. My aim was to
> define an area (a System.Address for example) in a control block
> record that the task manager would have space for "a task". What this
> task was, what it did, where it came from the task manager would not
> know nor care, but it would be the owner, or master, in some sense so
> the "preprocess" or initialization component could exit. I hope this
> makes a little sense.
There's still a lot of implementation in here. Talking about procedure
pointers and addresses is not only implementation, it's quite low level.
One of Ada's great strengths is its ability to express, implement, and
use high-level abstractions. The low-level stuff is still available, but
generally best hidden well down in the implementation of such an
abstraction.
What I get from your description is: You have classes of computations
("subsystems"); each class can have numerous computations carried on at
the same time; each computation produces output; you want some of the
output from all the computations of the same class to be subjected to
collating.
Everything else seems like implementation detail that may be blocking
your view of a solution.
Steve has given you some ideas about how to abstract the idea of
computations and controlling their scheduling. He didn't have get into
the resulting output or how to subject some of it, within a class of
computations, to collating.
I would probably take a different approach than Steve, with different
subsystems more independent, in order to ease the collating within the
subsystems, but that may be more a matter of style than substance.
If you can produce high-level package specifications that only address
the high-level concepts of your problem, without worrying about
implementation (private parts, bodies), with a road map of how they work
together, you'll probably find that you can then work out a way to
implement them.
--
Jeff Carter
"Don't knock masturbation. It's sex with someone I love."
Annie Hall
45
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Fun with Tasking
2007-03-29 15:17 ` mhamel_98
2007-03-30 4:06 ` Steve
2007-03-30 5:46 ` Jeffrey R. Carter
@ 2007-03-31 12:08 ` Stephen Leake
2 siblings, 0 replies; 10+ messages in thread
From: Stephen Leake @ 2007-03-31 12:08 UTC (permalink / raw)
mhamel_98@yahoo.com writes:
> Anyhoo Jeff, I'll try to explain in more detail what my program is
> doing and hopefully you can see where I'm going wrong. The program
> has a number of packages, each handling what I call a subsystem, to be
> verbose: ephemeris, residual, vector, covariance and a few others
> (this sort of equates to the "exec" procedure in my sample). Each of
> these systems has a number of procedures of interest, like a
> differencing of quantities, a statistical analysis, etc. Now each of
> these procedures of each of these subsystems can have up to 60 (and
> more as we go on) objects - each one I handle as it's own task.
There's no reason for each of these things to be a _task_.
They should each be in separate _packages_, and be separate _objects_,
but not _tasks_.
Tasks are only needed for controlling timing, and then you need more
than one task only when there are truly asynchronous events. Or if you
want to gain speed by using parallel processors.
In the system you describe, there are bunch of calculations, that can
be done correctly if they are done serially.
Since you talk about multiple processors, you should have one task per
processor, and portion the computations among the tasks.
--
-- Stephe
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2007-03-31 12:08 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-28 15:53 Fun with Tasking mhamel_98
2007-03-28 16:55 ` Adam Beneschan
2007-03-28 17:53 ` mhamel_98
2007-03-28 20:06 ` Ed Falis
2007-03-28 22:17 ` Randy Brukardt
2007-03-29 4:59 ` Jeffrey R. Carter
2007-03-29 15:17 ` mhamel_98
2007-03-30 4:06 ` Steve
2007-03-30 5:46 ` Jeffrey R. Carter
2007-03-31 12:08 ` Stephen Leake
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox