Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!apple!agate!darkstar!cbmvax.cbm.commodore.com From: jesup@cbmvax.cbm.commodore.com (Randell Jesup) Newsgroups: comp.os.research Subject: Re: Toy Experiment with Shared Memory Programming Message-ID: <11570@darkstar.ucsc.edu> Date: 25 Jan 91 18:50:27 GMT Sender: usenet@darkstar.ucsc.edu Organization: Commodore, West Chester, PA Lines: 306 Approved: comp-os-research@jupiter.ucsc.edu In article <8754@darkstar.ucsc.edu> jms@central.cis.upenn.edu (Jonathan M. Smith) writes: >I, and others, have claimed that using shared memory support for >programming can reduce the copying required in IPC. I agree that it can, and have some proof (albeit from another OS). I rewrote your examples for AmigaDos (which is based on lightweight processes). I also coded up a null version (reads from input, writes to output) to subtract out the IO overhead reading and writing the input/output channels. I ran all IO to and from the ramdisk to reduce FS overhead (the ramdisk does about 3 to 5Mb/sec for read/write on this machine). Times for a 2.5Mb file were (averaged over several runs): null: 0.60 sec msg: 0.70 sec pipe: 2.92 sec So, it looks like .10 seconds for msg, versus 2.32 seconds for pipes. I think 23:1 is a pretty clear win for shared memory. Note that on a system built around shared memory, there's no need to reinvent the wheel in order to have processes share memory. I simply pass messages back and forth telling the child the buffer is available and how many bytes are in it. Messages are shared and the entire OS is built on the message primitives (GetMsg, PutMsg, ReplyMsg, WaitPort). It took no longer for me to write the shared-memory version than to write the pipe-version (AmigaDos uses named pipes). BTW, PutMsg in AmigaDos merely links a message into the list of messages waiting at a port, and does whatever notification is needed (usually a signal to a process). For those that care, this is an Amiga 3000, 25mhz '030, 8 meg of on-board 80ns SCRAM, 8 meg of expansion bus ram (Zorro-III bus), about 1/2 as fast as the on-board ram. All execution was out of the on-board ram. The version AmigaDos is 2.0. >6. Summary (so far) >But it is helpful to observe the following facts: >1. IPC may not be the dominant cost in any case (here, it's external I/O) In many cases, true. However, extensive use of shared-mem techniques can cause large improvements in OS/application performance, especially on a system where most IO is handled by FileSystem processes and device processes (i.e. not running as part of a kernel, nor special in any way). This can be quite important in interactive processes, especially graphical ones where any delay in response is noticable and annoying to the user. >2. There was no major difference in the response times of "mem" and "piper" In my case there was (including FS overhead, it's still 3:1). >3. This may be a best case comparison for pipes - pipes are highly optimized >on UNIX and pipe flow control is tightly integrated with scheduling and >process control. And of course AmigaDos is optimized for fast task-switching (where most of that .1 seconds went), and fast messaging. Pipes are just another user-mode filesystem. Most Unix systems have more state to switch (and caches to flush: with LW processes no flush is needed). Here's the source code for those that are interested: /* pipe.c */ #include #include #include #include #include #define PIPESIZ 8192 #define PIPENAME "Pipe:bar" extern struct DosLibrary *DOSBase; struct Process *parent; int sig; void child(); struct TagItem tags[] = {NP_Entry,child, NP_Output,NULL, NP_CloseOutput,FALSE, TAG_END,0 }; char buffer[PIPESIZ]; int main (argc,argv) { BPTR pipe,input; struct Process *p; LONG len; parent = (struct Process *) FindTask(0); input = Input(); if (!(pipe = Open(PIPENAME,MODE_NEWFILE))) { printf("Can't open pipe!\n"); return RETURN_ERROR; } if ((sig = AllocSignal(-1)) == -1) { printf("Can't get signal?\n"); Close(pipe); return RETURN_ERROR; } SetSignal(0,1< 0) { Write(pipe,buffer,len); } if (len < 0) PrintFault(IoErr(),"Error on Read:"); Close(pipe); Wait(1< 0) { Write(output,child_buffer,len); } Close(pipe); Signal(parent,1< #include #include #include #include #include #include #define PIPESIZ 8192 #define PIPENAME "Pipe:bar" extern struct ExecBase *SysBase; extern struct DosLibrary *DOSBase; struct Process *parent; LONG sig; void child(); struct TagItem tags[] = {NP_Entry,child, NP_Output,NULL, NP_CloseOutput,FALSE, TAG_END,0 }; char buffer[PIPESIZ]; int main (argc,argv) { struct Process *p; struct Message msg; struct MsgPort *port; BPTR input; LONG len; parent = (struct Process *) FindTask(0); input = Input(); if ((sig = AllocSignal(-1)) == -1) { printf("Can't get signal?\n"); return RETURN_ERROR; } SetSignal(0,1<pr_MsgPort); while ((len = Read(input,buffer,PIPESIZ)) > 0) { msg.mn_Length = len; PutMsg(port,&msg); WaitPort(&(parent->pr_MsgPort)); /* wait for reply */ GetMsg(&(parent->pr_MsgPort)); } if (len < 0) PrintFault(IoErr(),"Error on Read:"); msg.mn_Length = 0; /* tell child to exit */ PutMsg(port,&msg); WaitPort(&(parent->pr_MsgPort)); GetMsg(&(parent->pr_MsgPort)); Wait(1<mp_Node.ln_Name = PIPENAME; AddPort(port); /* add to system list */ Signal(parent,1<mn_Length) { Write(output,buffer,msg->mn_Length); ReplyMsg(msg); } else { ReplyMsg(msg); break; /* EOF */ } } RemPort(port); DeleteMsgPort(port); Signal(parent,1< #include #include #include #include #define PIPESIZ 8192 extern struct DosLibrary *DOSBase; char buffer[PIPESIZ]; int main (argc,argv) { BPTR output,input; LONG len; input = Input(); output = Output(); while ((len = Read(input,buffer,PIPESIZ)) > 0) { Write(output,buffer,len); } if (len < 0) PrintFault(IoErr(),"Error on Read:"); return RETURN_OK; }