home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / mach / doc / unpublished / netmemorysrv.doc.Z / netmemorysrv.doc
Encoding:
Text File  |  1992-09-01  |  64.4 KB  |  1,248 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.              Design, Implementation, and Performance Evaluation of
  17.                   a Distributed Shared Memory Server for Mach
  18.  
  19.  
  20.         Alessandro Forin, Joseph Barrera, Michael Young, Richard Rashid
  21.  
  22.  
  23.                                   August 1988
  24.  
  25.                                  CMU-CS-88-165
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.                           Computer Science Department
  36.                           Carnegie-Mellon University
  37.                              Pittsburgh, PA 15213
  38.  
  39.  
  40.               A shorter version of this report will appear in the
  41.             1988 Winter USENIX conference, San Diego, January 1989
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49. Copyright (C)  1988 Alessandro Forin, Joseph Barrera, Michael Young, Richard Ra
  50.  
  51.  
  52. This  research  was  sponsored by the Defense Advanced Research Projects Agency
  53. (DOD), Arpa Order No. 4864, monitored by the Space and  Naval  Warfare  Systems
  54. Command  under  contract  number  N00039-87-C-0251.   The views and conclusions
  55. contained in this  document  are  those  of  the  authors  and  should  not  be
  56. interpreted as representing the official policies, either expressed or implied,
  57. of the Defense Advanced Research Projects Agency or the US Government.
  58.  
  59.                                    ABSTRACT
  60.  
  61. This report describes the design, implementation and performance evaluation  of
  62. a  virtual  shared  memory  server  for  the Mach operating system.  The server
  63. provides unrestricted sharing of read-write memory  between  tasks  running  on
  64. either  strongly  coupled  or  loosely  coupled  architectures, and any mixture
  65. thereof.  A number of memory coherency algorithms  have  been  implemented  and
  66. evaluated,  including  a  new distributed algorithm that is shown to outperform
  67. centralized ones.  Some of the features  of  the  server  include  support  for
  68. machines  with  multiple  page  sizes, for heterogeneous shared memory, and for
  69. fault tolerance.  Extensive performance measures of applications are presented,
  70. and the intrinsic costs evaluated.
  71.  
  72. 1. Introduction
  73.   Shared  memory  multiprocessors are becoming increasingly available, and with
  74. them a faster way to program applications and system services via  the  use  of
  75. shared  memory.  Currently, the major limitation in using shared memory is that
  76. it is not extensible network-wise, and therefore is  not  suited  for  building
  77. distributed  applications  and  services.  Example uses of a distributed shared
  78. memory facility include operating system services  such  as  file  systems  and
  79. process  migration,  distributed  databases,  parallel  languages  like  Ada or
  80. Multilisp, and systems for parallel  and  distributed  programming [11, 2, 10].
  81. More  motivation  for  a  distributed  shared  memory  facility  comes from the
  82. increasing  interest  that  hardware  designers  show  in   non-shared   memory
  83. multiprocessors:    the  Nectar project [1] at CMU for instance uses fast fiber
  84. optic links.  This will reduce the end-to-end time to send a  1  kilobyte  data
  85. packet  from the tens of milliseconds range of the current ethernet to the tens
  86. of microseconds range of the  fiber.    Fast  communication  makes  distributed
  87. shared memory an appealing complement to message passing.
  88.  
  89.   The  Mach virtual memory system allows the user to create memory objects that
  90. are managed by user-defined processes  (external  pagers) [13].    An  external
  91. pager  is  a  process responsible for providing data in response to page faults
  92. (pagein) and backing storage for page cleaning (page-out) requests.    This  is
  93. precisely  the  function  of  the in-kernel disk pager.  The only difference is
  94. that the user-specified pager task can manage the data in  more  creative  ways
  95. than  the  designer  of  the  in-kernel  pager may have envisioned.  This paper
  96. describes the design, implementation, and performance evaluation  of  one  such
  97. memory  server  which  provides  a  shared  memory semantics for the objects it
  98. manages.  The server provides unrestricted sharing of read-write memory between
  99. tasks  running  either  on  the  same machine or on different machines.  In the
  100. first case, all  processors  have  direct  access  to  common  physical  memory
  101. (architectures  with  Uniform  Memory  Access  time (UMA) or Non-Uniform Memory
  102. Access time (NUMA)) and the server provides a  flexible  management  of  shared
  103. memory.    In  the second case, processors do not have any way to access common
  104. memory (architectures with No Remote Memory  Access  (NORMA))  and  the  server
  105. provides it in software, migrating and replicating virtual memory pages between
  106. processors as needed.
  107.  
  108.   To understand the properties of a  distributed  shared  memory  facility  the
  109. performance  characteristics  of  the  server  itself  and  of some application
  110. programs have been evaluated.    To  measure  the  effects  of  different  page
  111. management policies in the server, a number of algorithms have been implemented
  112. and  evaluated,  including  a  new  distributed  algorithm   that   outperforms
  113. centralized  ones.    Some  of the features of the algorithms described include
  114. support for machines with differing page sizes, for  heterogeneous  processors,
  115. and  for  fault  tolerance.    The  algorithms  service page faults on multiple
  116. machines by migrating pages that must  be  shared,  scheduling  conflicting  or
  117. overlapping requests appropriately, tagging and translating memory pages across
  118. incompatible processors and  keeping  a  duplicate  copy  in  case  of  machine
  119. crashes.    The  experiments  with application programs were designed under the
  120. assumption that  the  amount  of  information  that  is  communicated  in  each
  121. synchronization  operation  is  the key factor.  Applications at the extreme of
  122. the spectrum were selected for testing.
  123.  
  124. 2. Shared Memory Within a Machine
  125.   The first goal of the server is  to  provide  sharing  of  read/write  memory
  126. between  tasks allocated on the same machine.  This overcomes the constraint of
  127. the standard Mach memory inheritance mechanism that the shared memory must have
  128. been  allocated  by  some  common  ancestor, as well as a security check in the
  129. implementation of the Unix exec(2) system call  that  deallocates  all  of  the
  130. task's  address  space.    The  server  provides the user with a call to create
  131. memory objects, logical pieces of memory that  are  identified  by  ports.    A
  132. memory  object  can  be  used  by  a  thread  in  a call to the vm_map() kernel
  133. primitive, which maps some portion of the object into the task's address  space
  134. at  some  virtual address.  Note that since a port can only be transmitted in a
  135. message, memory objects are entities protected by the kernel.  Note  also  that
  136. access to ports can be transmitted over the network, and therefore the vm_map()
  137. primitive allows for networked shared memory.
  138.  
  139.  
  140. From user to pager
  141.  
  142. create_memory_object( initial size )
  143.                 RPC,  Creates  a  new  memory object and returns the associated
  144.                 port.
  145.  
  146. memory_object_replicate( object )
  147.                 RPC,  When  using the distributed pager, create a local copy of
  148.                 the memory object.
  149.  
  150. memory_object_tag( tag, page range )
  151.                 RPC,  When using heterogeneous processors, assign a type tag to
  152.                 a portion of the memory object.
  153.  
  154. From user to kernel
  155.  
  156. vm_map( task, memory_object, address range, attributes )
  157.                 RPC, Maps an object into a task's address space.
  158.  
  159. vm_deallocate(task, address range)
  160.                 RPC, Removes all mappings for the given address range.
  161.  
  162. From kernel to server
  163.  
  164. memory_object_init(pager, control_port)
  165.                 MSG,  Contact  the  pager  of an object which is mapped for the
  166.                 first time, for initial handshake.
  167.  
  168. memory_object_data_request( page range, protection )
  169.                 MSG,  Request  for  a (range of) page which the kernel does not
  170.                 have in its cache.
  171.  
  172. memory_object_data_unlock( page range, protection )
  173.                 MSG, Requires more access permissions for a page.
  174.  
  175. memory_object_data_write( page range, pages )
  176.                 MSG, Pageout of dirty pages from main memory.
  177.  
  178. memory_object_lock_completed( page range )
  179.                 MSG, Completion of the requested paging operation.
  180.  
  181. memory_object_terminate()
  182.                 Notification of removal from cache.
  183.  
  184. From server to kernel
  185.  
  186. memory_object_set_attributes( attributes )
  187.                 MSG,   Confirms   availability  completing  initial  handshake,
  188.                 specifies initial attributes.
  189.  
  190. memory_object_data_provided( page range, pages )
  191.                 MSG, Provides page(s) data to the cache.
  192.  
  193. memory_object_data_unavailable( page range )
  194.                 MSG, Zero fill page(s).
  195.  
  196. memory_object_lock_request( object, request, reply_port )
  197.                 MSG,  Cache  control  request,  e.g.  page flush or granting of
  198.                 write access.
  199.  
  200.               Figure 1:   Summary Of The External Pager Interface
  201.  
  202.   The thread can access the memory  normally,  and  the  kernel  delegates  the
  203. paging  duties  to  the  user-level  memory  manager  (external  pager) that is
  204. responsible for the memory object.  This is done via  an  asynchronous  message
  205. protocol  between the pager and the kernel which is described in more detail in
  206.  [13].  The external pager interface allows pagers to control the  managing  of
  207. main  memory  by  the  kernel, so that main memory effectively acts as a common
  208. cache for memory objects.  The various operations  have  the  flavor  of  cache
  209. control  functions:   when a thread first accesses a page it takes a page fault
  210. and the kernel sends to the pager  a  memory_object_data_request()  message  to
  211. request  the  missing  page,  which  is  similar  to  a cache miss.  The server
  212. provides the page in a memory_object_data_provided() message.   Other  messages
  213. allow  a pager to request a page flush or specify the caching and copy policies
  214. for the object.  Figure 1 informally lists the messages  and  remote  procedure
  215. calls  defined by the external pager interface and by the virtual shared memory
  216. server.
  217.  
  218. 3. A Simple Algorithm
  219.   The shared memory server has been structured in an  object-oriented  fashion,
  220. so that it is possible to have memory objects with different behaviors.  When a
  221. memory object is mapped by tasks on  multiple  machines,  the  pager  needs  to
  222. manage  multiple  copies  of  memory  pages  in some coherent way.  The various
  223. management  policies   for   memory   objects   are   provided   by   different
  224. implementations  of  a  common  set  of operations: an implementation is called
  225. fault scheduler in the following, because the goal of the module is to schedule
  226. read  and write faults on different kernels in the best way, just like ordinary
  227. schedulers schedule the execution order of various threads.  One  of  the  many
  228. reasons for this choice is to allow experimentation with various algorithms and
  229. heuristics.  At  object  creation  time,  a  user  can  choose  which  specific
  230. scheduling  policy  will  be  applied to the new object, or rely on the default
  231. one.  All the algorithms we describe maintain strict memory  coherence  on  the
  232. objects  they  manage.    There is no stale data because at any given time each
  233. page exists in only one version.
  234.  
  235.   This Section describes a very simple  scheduler  that  provides  centralized,
  236. single  page-size memory objects.  There is only one pager task for each memory
  237. object, but different objects might be allocated to different  pager  tasks  to
  238. reduce  service  contention.    Since  Mach  IPC  is  location transparent, the
  239. location of the pager task is also transparent to the client kernels.  A  later
  240. Section  will  describe  how  this  algorithm is modified to allow distributed,
  241. coordinated management of a single object between separate pagers on  different
  242. machines.    Ownership  of  a page is transferred among kernels on demand:  the
  243. owner of the page is the kernel that currently has write access  to  the  page.
  244. When  no  kernel  has write access to a page the scheduler itself is the owner,
  245. multiple kernels are allowed to have read-only copies of the page.  The  simple
  246. scheduler's  algorithm  is  an  automaton  with  four  per-page  states,  which
  247. correspond to the four conditions in which a page can be:
  248.  
  249.    - Read: There are no writers, there may be readers  with  a  copy,  the
  250.      server has a valid copy. This is the initial state.
  251.  
  252.    - Write: There is one writer, there are no readers and no one is queued
  253.      waiting, the server does not have a valid copy.
  254.  
  255.    - ReadWait: There is one writer, some readers are waiting,  the  server
  256.      does  not have a valid copy and has asked the current owner to return
  257.      the page to the server.
  258.  
  259.    - WriteWait: There is one writer,  some  writers  are  queued  waiting,
  260.      there  may  be readers waiting, the server does not have a valid copy
  261.      and has asked the current owner to return the page to the server.
  262.  
  263.   Transitions between states are driven by the requests that are made by client
  264. kernels.    In  practice,  not  all  requests  make  sense  in all states.  For
  265. instance, a kernel will  not  pageout  a  page  that  has  not  been  modified.
  266. [Security issues have not been addressed directly in the server.  Rather, it is
  267. assumed that other servers, for example  a  name  service  integrated  with  an
  268. authentication  service,  will  do the necessary verifications before handing a
  269. memory object port to a user.   An  object  might  then  have  different  ports
  270. associated  with  it,  one  for read-only access and one for read-write access.
  271. Note then that it is possible to prevent a user from impersonating a kernel  by
  272. having  a secure server handle the object's port directly, and never permitting
  273. to unsecure tasks direct access to the  port.    These  and  other  issues  are
  274. addressed  in  a  forthcoming  document [14]  ]  The  server accepts four input
  275. message types (requests), which the scheduler handles in three procedures:
  276.  
  277.    - read_fault():   a   kernel   requests   read   access   to   a   page
  278.      (memory_object_data_request).
  279.  
  280.    - write_fault():  a  kernel  requests write access to a page and either
  281.      needs a fresh copy of the page (memory_object_data_request)  or  does
  282.      not (memory_object_data_unlock).
  283.  
  284.    - pageout():   a   kernel   flushes   out   the   page  to  the  server
  285.      (memory_object_data_write and memory_object_lock_completed).
  286.  
  287.   These three functions do all the necessary work.  A pseudo  code  description
  288. of how they operate on a page appears in Figures 2, 3 and 4.  It can be assumed
  289. that all procedures keep the page locked and that messages are processed in the
  290. order  of  arrival.   This pseudo code will be used again later to describe the
  291. distributed algorithm.  The remaining procedures are either for initialization,
  292. termination,  or  recovery from kernel crashes.  The pseudo code indicates that
  293. writers are queued in FIFO order, while readers do  not  need  to  be  ordered.
  294. Writers  take  precedence  over  readers.    Other,  possibly  more complicated
  295. policies might be needed. It is possible, for example, to  introduce  a  simple
  296. test  to prevent writers from causing starvation of readers.  Sections 3.1 will
  297. expand on the queueing strategies.  If we ignored fault  tolerance  issues  the
  298. algorithms  would  differ only in a minor way: the server can dispose of a page
  299. once it is sent to a writer.   This  and  other  optimizations  can  be  easily
  300. applied  in the case the server runs without the (local) support of a permanent
  301. storage server, which is the case of a diskless workstation.
  302.  
  303.  
  304.        read_fault(page, kernel)
  305.             switch ( page->state ) {
  306.             case Read:
  307.                     memory_object_data_provided(kernel)
  308.                     break
  309.             case Write:
  310.                     page->state = ReadWait
  311.                     memory_object_lock_request(page->owner, FLUSH(page), ow
  312.                     break
  313.             default: /* just enqueue */
  314.             }
  315.             set_add(page->readers, kernel)
  316.  
  317.  
  318.                       Figure 2:   Handling of Read Faults
  319.  
  320.   An example will help clarify the following discussion.  Since all  the  tasks
  321. on  one  machine  use  the same copy of the memory object's pages (cached copy,
  322. possibly mapped into the various address spaces with different protections), we
  323. can  pretend  there  is a single task per machine.  Let us assume that a thread
  324. makes a read access to a page.  The page is not in the cache, hence the  kernel
  325. sends  a  memory_object_data_request() message to the pager.  If the page is in
  326. Read state (the initial state), the server immediately  sends  the  page  in  a
  327. memory_object_data_provided()  message,  with  read-only  protection.    If the
  328. thread   makes   a   subsequent   write   access,   the    kernel    sends    a
  329. memory_object_data_unlock()  message to request a protection upgrade which will
  330. be granted in a  memory_object_lock_request()  message,  unless  the  page  has
  331. changed  state in the meantime.  If the page is not in Read state, the kernel's
  332. request is enqueued and possibly the current writer is asked to  page  out  the
  333. page  via  a  memory_object_lock_request()  message.  When the page is actually
  334. paged out, the pageout procedure dequeues the next  write  access  request  and
  335. satisfies it, or satisfies all read requests at once.
  336.  
  337.  
  338.        write_fault(page, kernel)
  339.             switch ( page->state ) {
  340.             case Read:
  341.                     set_remove( page->readers, kernel)
  342.                     forall( readers )
  343.     (1)                     memory_object_lock_request( reader, FLUSH(page)
  344.                     page->readers = empty_set
  345.     (2)
  346.                     page->state = Write
  347.                     page->owner = kernel
  348.                     if (needs_data)
  349.                             memory_object_data_provided( page->owner )
  350.                     else
  351.                             memory_object_data_unlock( page->owner )
  352.                     break
  353.             case Write:
  354.                     memory_object_lock_request( page->owner, FLUSH(page), o
  355.                     /* fall through */
  356.             case WriteWait:
  357.             case ReadWait:
  358.                     page->state = WriteWait
  359.                     enqueue( kernel, page->writers )
  360.             }
  361.  
  362.                      Figure 3:   Handling of Write Faults
  363.  
  364.  
  365.        pageout(page, kernel, data)
  366.     (3)     switch( page->state ) {
  367.             case Read:
  368.                     return  /* never happens */
  369.             case Write:
  370.                     save(data)  /* true pageout */
  371.                     page->state = Read
  372.                     page->owner = owner_self
  373.                     break
  374.             case WriteWait:
  375.     (4)
  376.                     save(data)
  377.                     page->owner = dequeue( page->writers )
  378.                     memory_object_data_provided( page->owner)
  379.                     if (!page->writers)
  380.                             if (page->readers)
  381.                                     page->state = ReadWait
  382.                             else
  383.                                     page->state = Write
  384.                     if (page->readers || page->writers) {
  385.                             deschedule_myself()
  386.                             memory_object_lock_request( page->owner, FLUSH(
  387.     (5)
  388.                     }
  389.                     break;
  390.             case ReadWait:
  391.                     save(data)
  392.                     forall(readers)
  393.                             memory_object_data_provided(reader)
  394.                     page->state = Read
  395.     (6)             page->owner = owner_self
  396.             }
  397.  
  398.  
  399.                    Figure 4:   Handling of Pageout Requests
  400.  
  401.  
  402.  
  403. 3.1. Multiple Page Sizes
  404.   The  simple  scheduler  described above can only be used by machines with the
  405. same page size, an unpleasant restriction.  Moreover, in Mach  the  size  of  a
  406. virtual  page can be changed and set even on a per-machine basis.  Transforming
  407. a single page size scheduler  into  a  multiple  page  size  scheduler  is  not
  408. immediate.   Our multiple page size scheduler uses internally an arbitrary page
  409. size (scheduler page size) and solves the problem by two means:
  410.  
  411.    - for requests smaller than the scheduler page  size,  the  request  is
  412.      rounded up to the scheduler page size, and
  413.  
  414.    - for  requests  larger  than  the  scheduler page size, the request is
  415.      fulfilled by multiple scheduler pages (shipped all  at  once),  after
  416.      appropriate synchronization.
  417.  
  418.   Synchronization is accomplished via a queueing mechanism.  It is necessary to
  419. avoid both false  contention  and  descheduling  of  kernels  until  absolutely
  420. necessary,  and  to  satisfy  requests as quickly as possible while maintaining
  421. fairness.  When the scheduler receives a request from a kernel, it may take one
  422. of the following actions:
  423.  
  424.    1. Satisfy the request immediately.
  425.  
  426.    2. Deschedule some writers and enqueue the request.
  427.  
  428.    3. Simply enqueue the request.
  429.  
  430.   The  first  is the case when there are no writers on any of the data that the
  431. kernel requests.  For a read request, the scheduler can simply add  the  kernel
  432. to  the  set  of  readers  of  each  scheduler-page;  if the request is a write
  433. request, then the scheduler deschedules all readers of  any  scheduler-page  in
  434. the  writer's  request range before scheduling the writer.  In the second case,
  435. the scheduler finds that there are writers on some of the requested  data,  but
  436. none  of them have yet been descheduled. The scheduler deschedules the writers,
  437. and the request is queued.
  438.  
  439.   In the third case, the scheduler finds descheduled writers  on  some  of  the
  440. requested  data,  indicating  that other requests are already waiting for those
  441. scheduler-pages.  In this case, the scheduler does not deschedule the  rest  of
  442. the  writers because the requesting kernel is not yet ready to use their pages;
  443. the request is simply enqueued.  When a descheduled writer sends a confirmation
  444. (a  memory_object_lock_completed()  message),  the  scheduler finds the request
  445. that was awaiting it. If the confirmation was the last one that the request was
  446. waiting  for, then the scheduler satisfies the request (as in case 1 above) and
  447. checks to see if there are any more requests that might be satisfied as well.
  448.  
  449.   The data  structures  used  for  queueing  readers  and  writers  allow  most
  450. operations  to  occur in constant time, while some (such as determining whether
  451. an incoming request can be immediately satisfied) take time proportional to the
  452. number  of  scheduler pages in the request.  Each waiting client is represented
  453. by a record containing the identity of the requestor, a reference counter,  and
  454. a  pointer  to a linked list of requests that follow.  The reference counter is
  455. used to quickly test if the request can be satisfied.  When the request follows
  456. other  requests  the  counter represents the number of requests pointing to it;
  457. otherwise it is used  to  represent  the  number  of  outstanding  descheduling
  458. acknowledgements.    For  each  scheduler  page  there is also a pointer to the
  459. request waiting for an acknowledgement from the  writer  of  the  page,  and  a
  460. pointer  to  the  last request waiting for the page.  These pointers are set to
  461. nil if no such request exists.
  462.  
  463.  
  464.  
  465. 3.2. Heterogeneous Processors
  466.   Parallel programs that use a distributed shared memory facility should not be
  467. constrained  to  run  on  a  uniform  set  of processors.  Such a constraint is
  468. undesirable because as the  number  of  machines  available  at  a  given  site
  469. increases one typically observes an increased variation in their types as well.
  470. Unfortunately,  interfacing  heterogeneous  processors  not  only  creates  the
  471. problem  of  potentially  different  page  sizes,  but also raises the issue of
  472. different machine representations of data objects.  This  problem  goes  beyond
  473. the byte order problem, since different processors are free to assign any given
  474. meaning to any given sequence of bits.  A clear example is the case of floating
  475. point numbers.
  476.  
  477.   A  more  difficult  set  of problems arises from software data types.  Modern
  478. programming languages allow higher level types to be built on top  of  hardware
  479. types,  for  instance  in  composing  record  structures with diverse component
  480. types.  Quite often, the language definition does not specify how  these  types
  481. should be mapped to the hardware types, and the compiler is free to define this
  482. mapping as appropriate.  A well known consequence is that the different  fields
  483. of  a  record  in  the  C  language  may  be  allocated at different offsets by
  484. different compilers, sometimes  even  among  compilers  for  the  same  machine
  485. architecture.    Finally,  some  languages  use  types  that  do  not  have any
  486. correspondent hardware type.  Lisp systems, for  instance,  often  use  runtime
  487. data  tags to mark a collection of bits as the representative of some data type
  488. (see [12] for a recent analysis).  Only a few processors implement some form of
  489. data tagging in hardware.
  490.  
  491.   Solving  the  heterogeneity problem is difficult because it requires that the
  492. server  has  knowledge  of  the  application's  data  types.    This  leads  to
  493. undesirable  close  links with the application's runtime system and programming
  494. language [2].  On  the  other  hand,  the  problem  can  be  separated  in  two
  495. sub-problems:    hardware  data  types  (e.g. integers) and software data types
  496. (e.g. C records).  A general purpose server solves the problems for  the  first
  497. class  of  types,  and  can be extended to cope with the second class of types.
  498. Quite simply, our server assigns a type tag to each segment of a paging  object
  499. and  makes  the  appropriate  translation (if necessary) when sending data from
  500. that segment to a kernel.   The  interface  with  the  application  program  is
  501. defined  by  the memory_object_tag_data() RPC from the client to the pager that
  502. assigns a type tag to a segment.  This operation is typically used by a dynamic
  503. memory  allocator  to  fragment  shared  memory in typed segments, each segment
  504. containing only data of the given type.  The standard Unix BSD malloc(2) memory
  505. allocator  for  C was modified to allocate typed data, as exemplified in Figure
  506. 5.  Although different types cannot be mixed in a  structure,  one  can  always
  507. resort  to  a level of indirection, building records that only contain pointers
  508. to data.
  509.  
  510.  
  511.     extern char
  512.             *tmalloc( type_tag, num_elements )
  513.     enum { t_int8, t_int16, t_int32, t_float32, ... } type_tag;
  514.     unsigned long int num_elements;
  515.  
  516.     #define malloc_short(n) (short*)tmalloc( t_int16, n)
  517.     ...
  518.  
  519.  
  520.                          Figure 5:   A Typed malloc()
  521.  
  522.   All type tags and machine types must be known to the server in advance, hence
  523. each  server is able to deal with a limited set of machine and data types.  The
  524. server refuses type tags or machine types that it does not know how to  handle.
  525. This  limitation  is  not  very  restrictive:  since the server is a user level
  526. process it can be modified quite easily to account  for  new  data  or  machine
  527. types.    A  dynamic solution requires the use of runtime type descriptors that
  528. the server uses for data translation.  This approach is described  in [5],  and
  529. solves the problem of software data types as well.  It is certainly possible to
  530. extend our server in this way.
  531.  
  532.   Finally, note that an approach similar to the one used for  data  translation
  533. might  be  used  for  other  problems. Some approaches to the implementation of
  534. shared libraries require the use of a dinamic linker.  Dinamic linking could be
  535. done  using lazy-evaluation, only linking those pages of code that are actually
  536. accessed by the program when they are faulted in.  A similar case arises with a
  537. secure  program loader, which must check that the executable image has not been
  538. tampered with.  A distributed object system might also use  similar  techniques
  539. while mapping objects into the program's address space.
  540.  
  541. 4. A Distributed Algorithm
  542.   The  motivations  for  a  distributed  algorithm are manyfold.  A centralized
  543. server is a solution that does not scale up.   When  many  kernels  share  many
  544. memory  objects  serviced  by  the  same  pager the availability of each object
  545. decreases, because the pager becomes the bottleneck where all requests pile up.
  546. Even  when  few  kernels  are involved, the location of the server is important
  547. because local  and  remote  messages  might  have  very  different  costs.    A
  548. distributed  solution  that can allocate any number of servers on any number of
  549. machines is more usable.  In this way  the  sharing  of  memory  between  tasks
  550. located  on  the  same  (multi)processor  is decoupled from unrelated events on
  551. other machines.  A careful analysis of the external  pager  protocol [13]  also
  552. reveals  one inefficiency:  transferring ownership of a page from one kernel to
  553. another requires four messages (requesting the page,  obtaining  it,  receiving
  554. the  end-of-transfer  message, shipping it to the right kernel), while only two
  555. messages are strictly needed (request the  page  transfer,  ship  it  from  one
  556. kernel  to  the  other).  Rather than modifying the external pager interface to
  557. handle this case, we have designed and implemented a distributed paging  server
  558. which  exploits  this  and  various  other  opportunities  for reducing network
  559. traffic.
  560.  
  561.   The approach taken is simple: treat each  remote  server  just  like  another
  562. kernel,  and  apply the algorithm of the centralized case.  The reader may wish
  563. to go back to Figures 2, 3 and 4 and review the algorithm substituting the word
  564. "kernel"  with  "client",  which  now  means either a kernel or (more likely) a
  565. fellow server.  A pager will now accept a memory_object_lock_request()  message
  566. just  like  a  Mach  kernel does and treat it as a fault notification, invoking
  567. read_fault() or write_fault() as appropriate.  A  memory_object_data_provided()
  568. message is handled by the pageout() procedure.
  569.  
  570.   Note  now that the notion of the "owner" that each pager has does not need to
  571. be exact at all times.  It is quite possible, actually highly desirable, that a
  572. pager  be able to ask a second pager to transfer a page directly to a third one
  573. who needs it, without handling the page directly.  We  call  this  optimization
  574. forwarding,  to catch both the positive effect of avoiding one message hop, and
  575. the (minor) negative effect of producing a new type of  activity:  the  act  of
  576. forwarding  a  mis-directed  page  fault  message  to  the correct destination.
  577. Implementing forwarding requires relatively simple changes to  the  centralized
  578. algorithm.
  579.  
  580.  
  581.     (1)     memory_object_lock_request( reader, FLUSH(page),
  582.                     is_server(page->owner) ? kernel : owner_self)
  583.  
  584.     (2)     if (page->owner != owner_self) {
  585.                     memory_object_lock_request( page->owner, WRITE_FAULT(pa
  586.                     enqueue(page->writers, kernel)
  587.                     page->state = WriteWait
  588.                     return
  589.             }
  590.  
  591.     (3)     if (kernel != page->owner && !hinted(page))
  592.                     page->owner = kernel
  593.             hinted(page) = FALSE
  594.  
  595.     (4)     if (!page->writers) {
  596.                     page->owner = owner_self
  597.                     goto ReadWait
  598.             }
  599.  
  600.     (5)     if (is_server(page->owner))
  601.                     page_state = WriteWait  /* pretend */
  602.  
  603.     (6)     if (!is_server(kernel))
  604.                     page->owner = owner_self
  605.  
  606.  
  607.             Figure 6:   Modifications to the Distributed Scheduler
  608.                           to Implement Forwarding of Page Faults
  609.  
  610.   Figures  6  and  7 illustrate the changes and additions to the pseudo code of
  611. Figures 2, 3 and 4 to implement forwarding.  A pager creates a local copy of  a
  612. memory  object when a user asks for it.  The initial state of all pages in this
  613. case is the Write state, and the owner is the pager from which the  object  has
  614. been  copied.  Of  course,  no  real  copy  is  actually done.  Note that it is
  615. possible to copy from another copy, and that the pager does not  need  to  have
  616. complete  knowledge  of  all the kernels involved.  The handling of read faults
  617. does not change.  While handling write faults, at  line  (1)  all  readers  are
  618. informed  of  who the new owner is, if it is a different pager.  At line (2), a
  619. check is added to see whether the true owner  actually  is  another  pager,  in
  620. which  case the fault is queued and the state of the page modified accordingly.
  621. In the pageout() procedure at line (3) it is necessary to handle the case where
  622. the  pager has incorrect information about the true owner.  Note that the pager
  623. might have received a hint about who will eventually become the  owner  because
  624. it  forwarded  a  write fault.  At line (5) it is necessary to handle specially
  625. the case when a page is given to a server  queued  for  writing,  while  having
  626. other  readers  waiting.   The immediate request to have the page back pretends
  627. that there are writers queued anyway, to prevent the race that would  otherwise
  628. arise.  Line (4) jumps to the correct code in case the last writer had actually
  629. been serviced.  Line (6) handles the fact  that  if  the  pager  only  receives
  630. read-only access to the page it does not become the owner of the page.
  631.  
  632.   Two  new  procedures, described in Figure 7, are used to check whether a page
  633. fault must be forwarded and to handle invalidations  of  read-only  pages.    A
  634. memory_object_lock_request()  message  is  handled  first  by  the page_fault()
  635. procedure, which forwards it  if  necessary.    The  fault  is  definitely  not
  636. forwarded  if  the  pager  has  ownership of the page, or the pager has already
  637. asked the current owner for write access to the page (state WriteWait),  or  if
  638. the  pager  has  (state  Read) or is about to have (state ReadWait) a read-only
  639. copy of the page and the fault is a read fault.  In other  words,  a  fault  is
  640. only  forwarded to another server when the pager has no current interest in the
  641. page whatsoever.  An invalidation of a read-only page is generated at lines (1)
  642. and  (7)  if  the  reader  is a server, and is handled in the invalidate_page()
  643. procedure.  This is the only new message type needed.
  644.  
  645.   Forwarding creates problems for a closed form analysis, since the  effect  of
  646. forwarding  of both page locations (page faults) and invalidations (page flush)
  647. are difficult to model.  Our claim is that in actual use one will typically see
  648. only the two extreme cases: pages that are frequently accessed in write mode by
  649. many parties, and pages that are accessed infrequently,  most  likely  in  read
  650. mode.    Even  if  a  page  is  accessed infrequently, it is hard to generate a
  651. faulting sequence that produces  many  forwarding  messages.    This  claim  is
  652. supported  by  the  experience  with actual application programs.  Infrequently
  653. accessed pages do not affect performance.  The bottlenecks derive  very  easily
  654. from  the opposite case.  Our analysis shows that the expected number of remote
  655. messages required to service a N-party page fault for the distributed pager is
  656.  
  657.    - 3N-4 initially, and
  658.  
  659.    - 2N-1 or
  660.  
  661.    - 2N at steady state
  662.  
  663. depending on boundary conditions.  To get the total number of messages  in  the
  664. distributed  scheduler  one  must  add  a  total of 2N-2 local messages between
  665. pagers and the kernels they service.  For comparison, any centralized algorithm
  666. that maintains strict memory coherence must use at least 4N remote messages and
  667. no local messages. In the case of  the  simple  scheduler  this  figure  is  5N
  668. messages.  Since the cost of local messages is often much less than the cost of
  669. remote messages, the distributed pager clearly outperforms the centralized one.
  670. The  performance  evaluation  results,  reported  in  Section  7  confirm  this
  671.  
  672.  
  673.        invalidate_page(page, owner)
  674.             if (page->state != Read)
  675.                     return  /* sanity check */
  676.             forall (readers)
  677.     (7)             memory_object_lock_request(reader, FLUSH(page), owner)
  678.             page->state = Write;
  679.             page->owner = owner;
  680.  
  681.        page_fault( page, who, fault_type)
  682.             if ((page->owner == owner_self) ||
  683.                 !is_server(page->owner) ||
  684.                 (page->state == WriteWait) ||
  685.                 ((fault_type == READ) && (page->state != Write))) {
  686.                     if (fault_type == READ) read_fault(page, who)
  687.                     else write_fault(page, who)
  688.                     return
  689.             }
  690.             /* Forward */
  691.             send_page_fault(owner,who,page)
  692.             if (fault_type == WRITE) {
  693.                     page->owner = who
  694.                     hinted(page) = TRUE
  695.             }
  696.  
  697.  
  698.               Figure 7:   Additions to the Distributed Scheduler
  699.                           to Implement Forwarding of Page Faults
  700.  
  701. analysis.
  702.  
  703.  
  704.  
  705. 4.1. Example
  706.   When a thread first maps a memory object in  its  address  space  the  kernel
  707. contacts  the  server but does not require it to send any data yet.  It is only
  708. when a thread touches a memory location within  the  address  range  where  the
  709. object  is  mapped  that a fault is generated.  The faulting thread is stopped,
  710. and a message is sent to the pager to request data to service the fault.   When
  711. the  scheduling  algorithm  in  the server has the necessary data available the
  712. page is sent to the kernel which maps it for the faulting thread which can then
  713. continue  execution.    In  case  the  page is not immediately available at the
  714. server, a message is sent to the kernel that currently owns the page, asking to
  715. page  it out to the server.  In the case of the distributed algorithm, this may
  716. imply some more processing, since the "kernel" is actually another server.
  717.  
  718.   It is  interesting  to  consider  one  example  that  shows  the  effects  of
  719. forwarding page faults among distributed servers.  Let us assume that N servers
  720. (each one serving one or more kernels) all take repeated  page  faults  on  the
  721. same  page,  which  is  the  hotspot  case that makes distributed shared memory
  722. perform the worst.  Initially, all servers refer to the memory  object's  pages
  723. from the same one (say server 1).  Therefore N-1 requests are sent to server 1.
  724. The server first services its local fault(s), then ships the page to  server  2
  725. (say)  which  becomes  (in  server's  1 opinion) the new owner.  The next fault
  726. request is then forwarded by server 1 to server 2, the next to server 3 and  so
  727. on,  to  server  N-1.    When  all  faults  have been forwarded and served, the
  728. situation is such that servers 1, N-1 and N all know that the page  is  located
  729. at  server  N,  while  every other server i believes the page is at server i+1.
  730. When all servers take the next page fault only  2  requests  are  sent  to  the
  731. owner,  and  any other request i is queued at server i+1 waiting for i+1 itself
  732. to be served first.
  733.  
  734.         S1      S2 ->   S3 ->   S4 ->   ...     Sn-1 -> Sn
  735.         |                                               ^
  736.         -------------------------------------------------
  737.  
  738.          Figure 8:   Steady State Behavior for a N-Party Write Hotspot
  739.  
  740.   This situation  is  depicted  in  Figure  8  and  can  repeat  itself.    Our
  741. experiments  show  that indeed in a write-hotspot the system oscillates between
  742. two configurations of this type, never entering the initial state again.  There
  743. is  a  worst case that could surface:  an isolated page fault triggers a number
  744. of forwarding messages. This number is N-2, since always at least  two  servers
  745. know  exactly where the page is: the owner and the one who sent the page to it.
  746. In the example, this would happen if server 2 alone takes  a  fault  after  the
  747. first  N  faults are served.  After a worst case fault all servers know exactly
  748. where the page is, and therefore the system goes back to the initial state.
  749.  
  750. 5. Fault Tolerance
  751.   A network memory server must  be  prepared  to  handle  machine  crashes  and
  752. network  partitioning without deadlocking.  Once a crash has been detected, the
  753. server must either make  user  programs  aware  of  the  problem  (for  example
  754. signaling  a  memory  error), or attempt to recover from the problem one way or
  755. another.  Whatever action the server takes will not  provide  application-level
  756. fault  tolerance  since  the  crash  could  leave  memory inconsistent from the
  757. application's point of view.  This happens, for instance, when a kernel crashes
  758. and some shared memory lock was held by a thread running on that processor.
  759.  
  760.   The  centralized  schedulers provide a mechanism for surviving kernel crashes
  761. whereby memory availability is preserved despite a failure of the current owner
  762. of  a page.  This avoids the alternative of making the whole object permanently
  763. unavailable.  Assuming the current writer crashes (or for  any  reason  is  not
  764. capable  of  communicating  with the server any more) the server reverts to the
  765. latest copy it has of the page, which is the one that was sent  to  the  writer
  766. when  it  asked  for  write  permission.    Fault  tolerance mechanisms for the
  767. distributed scheduler have not yet been implemented, and they will need to face
  768. the problems of network partitioning as well.
  769.  
  770.   Failure  of  a  kernel only needs to be detected when the server needs a page
  771. back from it.  The overhead of a fault tolerance guard can therefore  be  quite
  772. limited, about 1% of our servers' time when heavily used.
  773.  
  774. 6. Related Work
  775.   Forwarding  creates  a  new  need:  the need of forwarding page faults to the
  776. current owner of a page.  Li [7] looked at the problem of locating a  page  and
  777. provided  various  algorithms  to  solve  it,  and  analyzed  their costs.  Our
  778. distributed  algorithm  must  be  compared  against  the  "Distributed  Manager
  779. Algorithm  2.6",  with  the  optimizations  indicated  at pages 61-63 that page
  780. invalidations are sent in a divide-and-conquer fashion.  Note however  that  in
  781. Li's  algorithms all operations are RPC, hence requiring twice as many messages
  782. and unnecessary serialization.  Li also evaluates the use of broadcast messages
  783. and proves that they could benefit some of his algorithms, under the assumption
  784. that their cost is the same as a direct message.  Note that  in  our  algorithm
  785. the use of broadcasts would be detrimental to performance, since it brings back
  786. the system to the initial state and away from  the  most  favorable  situation.
  787. The  idea  of  propagating invalidations in a divide-and-conquer fashion is, in
  788. our system, much more effective than broadcasts.  In this  paper  it  was  only
  789. assumed  that  the  underlying  architecture  provides efficient point-to-point
  790. communication, with quasi-uniform cost.  The cost of sending  a  message  to  N
  791. recipients  is therefore greater than or equal to N times the cost of a message
  792. to a single recipient.
  793.  
  794.   Cheriton [3] has recently extended the V kernel to  support  user-level  data
  795. and  caching  servers,  which can be used to provide distributed shared memory.
  796. His facility  has  many  similarities  with  Mach's  external  pager  facility,
  797. although  it  is  described  in  terms  of file abstractions rather than memory
  798. object abstractions.  The implementation uses a scheme analogous to the  simple
  799. scheduler  presented above, but might add considerable extra message traffic by
  800. polling and forcing page flushes every T-milliseconds to  provide  T-consistent
  801. files for transaction support.
  802.  
  803.   Fleisch [4]  has  extended  the  Locus  kernel  to provide distributed shared
  804. memory, with a SystemV interface.  The scheme  he  describes  seems  geared  to
  805. maintaining consistency at the segment rather than page level.  A report on the
  806. implementation work will be necessary to better evaluate his approach.
  807.  
  808.   Our work is concerned with Operating System level distributed shared  memory,
  809. where it is implemented as shared pages of virtual memory.  Other approaches to
  810. user-level shared memory objects are possible,  for  example  providing  shared
  811. data  structures  as in the Agora [2] system.  Other references can be found in
  812.  [2].
  813.  
  814. 7. Performance Evaluation
  815.   The performance of the server was evaluated along  a  number  of  dimensions.
  816. Fundamental  are  the average times to service a fault, in both cases of single
  817. machine and multi-machine applications.  These  are  affected  by  the  various
  818. special  features  of  the  server.  The centralized and distributed cases were
  819. compared, using ad-hoc programs  that  exercise  the  hotspot  behavior.    Our
  820. measures  show two overall results: the distributed algorithm is more efficient
  821. than the centralized one, and none of the special features we introduced has an
  822. unacceptable  impact  on  performance.    The  major  bottleneck  in  the  test
  823. configuration (token ring workstations) is the network latency, which  accounts
  824. for  about  98% of the elapsed times.  The server was instrumented in two ways:
  825. keeping track of the number and type of faults it services (per object and  per
  826. page),  and  collecting  extensive  traces of the message activity.  These data
  827. were obtained via a remote procedure call  by  other  processes,  with  minimum
  828. perturbation.
  829.  
  830.  
  831.  
  832. 7.1. Basic Costs
  833.   The  most  common  use  of  the  server  is in sharing memory within a single
  834. machine.  In this case, a fault on a missing  page  (cache-fill)  requires  two
  835. local  messages, for a total cost of 1.5ms on a IBM RT-APC.  A protection fault
  836. also requires two messages but no memory mapping, for  a  cost  of  1.1ms.    A
  837. pageout  operation  requires two receive messages and the deallocation of data,
  838. which is not a system call but a RPC to the kernel and involves two messages[As
  839. noted  later,  deallocation of memory is done by a separate thread, which means
  840. that for evaluating the latency of the server a value of 1.5ms must be used  ].
  841. The  total cost is then 2.5ms.  Since system time is by far the dominant factor
  842. (93%) in all cases, schedulers do  not  show  significant  differences  in  the
  843. handling of local faults.  Table 1 summarizes the most important costs.
  844.  
  845.   Memory  use  is  an  important factor for characterizing the performance of a
  846. program, although our primary concern was speed rather than space.  The  server
  847. allocates  memory  in  a sparse fashion only when a kernel demands it, and then
  848. replaces each page as it is paged out by a kernel.  This not only  reduces  the
  849. memory  usage for a large and sparse object, but also removes from the critical
  850. path the copying of data (just switch a pointer) and the deallocation of memory
  851. (two  messages)  which can be done in batches.  To quantify these improvements,
  852. the hotspot cycle time for the distributed case for the  simple  scheduler  was
  853. reduced  by  this  strategy  from  7.8ms/fault to 5.5ms/fault, including memory
  854. deallocations.  Memory deallocation can be devoted to a separate thread,  which
  855. reduces  the fault time to approximately 4.2ms/fault.  Memory saving depends on
  856. the actual use, and is very effective for some applications.
  857.  
  858.  
  859.  
  860. 7.2. Costs of The Algorithms
  861.   The multiple page size scheduler adds  some  overhead  to  the  fault  times,
  862. primarily  because  more  server pages might be needed to cover a kernel's page
  863. fault.  In most cases, a small range of page sizes will be used, but even  with
  864. an  unlikely  ratio  maximum/minimum  page  size of eight the overhead over the
  865. basic fault times is only 0.2ms.  If necessary, however, the algorithm  can  be
  866. tuned further for larger page size ranges.
  867.  
  868.   Various  experiments  were  performed  on the distributed scheduler, the most
  869. interesting one being the case of an hotspot page.  This is demonstrated  by  a
  870. simple  program that repeatedly increments the same memory location, replicated
  871.  
  872.  
  873.  
  874.  
  875. Parameter                               Measured Cost
  876.  
  877. Zero-fill Fault                         1.5ms/fault
  878.  
  879. Protection Fault                        1.1ms/fault
  880.  
  881. Hotspot Cycle                           4.2ms/cycle
  882.  
  883. Multiple Page Size Overhead             0.2ms/fault max
  884.  
  885. Avg Messages, centralized hotspot case   5.0/fault (all remote)
  886.  
  887. Avg Messages, distributed hotspot case   4.1/fault (2.0 remote)
  888.  
  889. Forwarded Faults                         10% (hotspot)
  890.  
  891. System Time                              93%
  892.  
  893.  
  894.  
  895.  
  896.                         Table 1:   Costs Of The Server.
  897.  
  898. on various machines.  The measures show that on average  each  server  received
  899. requests  for  read/write/protection  faults  in  an equal amount, as expected.
  900. This also means that the user program was interrupted 60%  of  the  times  when
  901. attempting  to  write  the  location  (write or protection fault), and 30% when
  902. reading from it (read fault).  The average number of messages per fault is  the
  903. single most important figure:  on average, each server handled 4.1 messages per
  904. fault.  Half these messages are received  and  half  sent.    On  average,  2.1
  905. messages  are  local  (interactions  with  the local kernel) and 2.0 are remote
  906. (interactions  with  other  servers).    This  nicely  confirms  the  estimates
  907. presented in Section 4. Remote messages are extremely more expensive than local
  908. ones: an average 98% overhead was observed in the test system, equally  divided
  909. among  the  local  Mach network server, the remote one, and the TCP/IP transfer
  910. protocol.
  911.  
  912.   The results indicate that the distributed algorithm makes the page  available
  913. in  a  fair fashion, in the sense that among homogeneous processors the page is
  914. made available for an equal amount of  time  to  all  kernels:  the  number  of
  915. operations for all programs were the same within a factor of 2%.  If processors
  916. of different speed are used, the time during which a page is available does not
  917. change  (it  is  bound by the network latency):  using a processor two times as
  918. fast on our RTs exactly doubles the number of operations in the user  programs.
  919. Other  measures  indicate  that during the experiment each server handled about
  920. 58% local faults and 42% remote faults, including a 10% of  requests  that  are
  921. forwarded.  The total number of faults was the same (within 2%) on all servers.
  922. Each server requested or provided the page the same  number  of  times  (within
  923. 3%), including the case of a mix of slow and fast processors.
  924.  
  925.  
  926.  
  927.  
  928. Machine             Int16/32            Float32             Float64
  929.  
  930. Sun 4/260 (*)       0.8                 1.0                 1.1
  931.  
  932. Vax 8800            1.5                 2.3                 3.7
  933.  
  934. IBM RT              1.9                 2.4                 2.5
  935.  
  936. Sun 3/280           1.9                 2.5                 2.9
  937.  
  938. @g(m)Vax-III        2.8                 4.6                 6.8
  939.  
  940. Sun 3/160           3.0                 4.8                 4.6
  941.  
  942. Vax 785             4.4                 7.6                 10.9
  943.  
  944. Encore (*)          4.9                 12.5                14.3
  945.  
  946. @g(m)VaxII          6.1                 10.4                14.5
  947.  
  948. Vax 8200            9.1                 15.3                27.9
  949.  
  950.  
  951.  
  952.  
  953.                    Table 2:   Overhead of Data Translations
  954.                              (in milliseconds per 4kbytes).
  955.  
  956.   For the heterogeneity problem, only those machine types that are more or less
  957. implied by the definition of the C language  were  chosen  for  implementation,
  958. which  means  integers of various sizes and floating point numbers.  Many other
  959. data types map obviously onto these types.  Support for software types  is  not
  960. provided.  For floating point numbers, the two formats that are most often used
  961. on our machines (Vax-D and IEEE-754) were selected.  Both short (32  bits)  and
  962. long  (64 bits) forms were considered.  Table 2 shows the overheads measured on
  963. the server on a wide variety  of  machines.    The  times  reported  are  those
  964. necessary  to  convert  4kbyte  of data, but note that some machines use larger
  965. page sizes.  There is no other basic overhead beyond a simple test  of  whether
  966. conversion is necessary or not.  Starred entries in the table indicate machines
  967. for which a Mach External Pager kernel is not yet available.  In these cases, a
  968. synthetic  test  was  run to time the critical code.  Note that the translation
  969. process is very much dependent on the processor type because  the  availability
  970. of  special  byteswap  instructions  can  speed  it  up  considerably.  This is
  971. demonstrated by the entry for the IBM RT.
  972.  
  973.   Assuming that the server's (multi)processor has spare cycles, it is  possible
  974. to  eliminate  the  type conversion overhead at the expense of increased memory
  975. usage.  The server can keep multiple copies of each segment,  one  per  machine
  976. type,  and  pre-translates  it when a page is received.  Translation is done in
  977. parallel by a separate thread, which works in a pipelined fashion with the main
  978. thread that services faults.  We have not yet implemented this optimization.
  979.  
  980.   In  the  centralized servers, the indicated overhead is paid each time a page
  981. is   sent   to   a    kernel,    as    added    time    for    executing    the
  982. memory_object_data_provided()  operation.   This means that both read and write
  983. faults are affected, for machines that are not of the same general type as  the
  984. object's creator.  There is no overhead for protection faults, or for identical
  985. or compatible machines like the case of two Vaxen or the case of a Sun  and  an
  986. IBM   RT.    Note,  however,  that  in  some  configurations  the  overhead  of
  987. byte-swapping and floating point conversion sum up: In  the  worst  case  of  a
  988. centralized  server  running  on  an  IBM  RT  and serving a Vax and an Encore,
  989. swapping is required both before  and  after  the  floating  point  conversion.
  990. Again,  the distributed server performs much better since translation is merged
  991. in the page replication process:  The  server  that  receives  a  page  from  a
  992. machine-incompatible  other  server  translates  it before forwarding it to the
  993. Mach kernel.  In this case, no more than one translation is ever required,  and
  994. read  or write faults do not require any translation at all when the server has
  995. a valid local copy of the page.
  996.  
  997.  
  998.  
  999. 7.3. Application Programs
  1000.   Intuitively, the performance gain from the use of memory  sharing  techniques
  1001. comes  from  the  large  amounts of information that can be transferred with no
  1002. cost between parallel activities in each synchronization operation.    Below  a
  1003. certain  threshold,  on  a  uniprocessor the integration of scheduling and data
  1004. transfer provided by a kernel optimized for message  passing  is  apparent  and
  1005. wins  over  the  simple  busy-waiting scheme of spin-locks.  The effect must be
  1006. visible in the networked case, where spin-locks are more expensive.   This  was
  1007. the  idea  that guided the choice of applications for testing the server.  This
  1008. hypothesis only partially contradicts  the  suggestion  that  the  locality  of
  1009. references would completely dominate performance of a distributed shared memory
  1010. program.
  1011.  
  1012.   In the networked shared memory case,  all  the  tasks  running  on  the  same
  1013. machine  produce  a  single load on the pager, and the advantage of one of them
  1014. obtaining a page that will then be used by other tasks is not apparent.    This
  1015. non-measurable  gain  was eliminated from the experiments and only one task was
  1016. allocated per machine even if this is clearly unfair to the pager.
  1017.  
  1018.   All programs have been developed for a uniform shared memory  multiprocessor,
  1019. and were not modified in any way to get better distributed performance.  In the
  1020. matrix multiplication case, the problem is  decomposed  so  that  each  machine
  1021. computes  all the elements of some row in the output matrix.  In this way it is
  1022. easy to compute large matrices with few processors.  The Shortest Path  program
  1023. is  a parallel version of a sequential algorithm which shows Nlog(N) complexity
  1024. for planar  graphs [6].    The  program  evaluates  in  parallel  the  possible
  1025. extensions  to  the  most  promising paths, and each activity only looks in the
  1026. neighborhood of a point and queues the new extensions to other activities.  The
  1027. other  two  programs  have  been  used  for  architectural  simulations, on the
  1028. assumption that they are representatives of a large class of parallel programs.
  1029. Mp3d is a particle simulator [8] and LocusRoute is a parallel VLSI router [9].
  1030.  
  1031.   The   experiments  were  performed  on  machines  under  standard  multi-user
  1032. operating conditions, including any necessary disk paging.  Measures were taken
  1033. of  elapsed  and  per-thread CPU times.  Table 3 shows the results of executing
  1034. the programs on a small group of IBM RTs on a token ring.  The network  latency
  1035. dominates  performance,  and  only  the matrix multiplication case shows linear
  1036. speedup.  All programs are known to demonstrate linear speedups on a  bus-based
  1037. shared memory multiprocessor with a small number of processors.
  1038.  
  1039.  
  1040.  
  1041.  
  1042. Program             1 Machine           2 Machines          3 Machines
  1043.  
  1044. Matrix 128x128      29                  15                  10
  1045.  
  1046. Matrix 256x256      241                 122                 80
  1047.  
  1048. ShortestPath        60                  60                  40
  1049.  
  1050. LocusRoute          277                 333                 397
  1051.  
  1052. Mp3d                8.6                 16.1                23.0
  1053.  
  1054.  
  1055.  
  1056.  
  1057.            Table 3:   Execution Times For Some Application Programs
  1058.  
  1059.   One  important  factor  affecting the performance of an application that uses
  1060. dynamically managed shared memory is the memory allocation algorithm used.   Li
  1061. described  a scheme for memory allocation derived from Knuth's FirstFit scheme.
  1062. A quick comparison was made with a  different  one,  a  descendant  of  Knuth's
  1063. FreeList  algorithm.    Such  an  allocator  is currently used, in a sequential
  1064. version, by the standard Berkeley BSD Unix distribution.   A  parallel  version
  1065. was  easily  created  by  associating  a  semaphore  to each free list, whereby
  1066. requests for memory blocks of different sizes proceed completely  in  parallel.
  1067. It is much more difficult to make the FirstFit scheme more parallel.
  1068.  
  1069.   The  measurements  show  that  not  only does the FreeList algorithm use less
  1070. memory (1/4 on average) than the FirstFit one, but  that  it  is  about  20-30%
  1071. faster  even  in  the  sequential case.  Other measurements indicate that a two
  1072. level memory allocation strategy is very effective in  reducing  shared  memory
  1073. contention.    The  simple  solution  of  allocating and deallocating memory in
  1074. batches for blocks of the most frequently used size often suffices to eliminate
  1075. the most obvious bottlenecks.
  1076.  
  1077. 8. Conclusions
  1078.   A  user-level  memory  server for Mach and the algorithms it uses for dealing
  1079. with issues like heterogeneity, multiple page sizes,  distributed  service  and
  1080. fault  tolerance  was  described.  The server shows very good performance under
  1081. all tests, and the distributed algorithm is effective in reducing communication
  1082. over  the  (potentially  slow)  communication medium.  Results with application
  1083. programs are dominated by the network latency, but still optimal in some cases.
  1084. It  is  conjectured  that  the amount of data exchanged between synchronization
  1085. points is the main indicator to consider  when  deciding  between  the  use  of
  1086. distributed shared memory and message passing in a parallel application.  There
  1087. is definitely space  for  more  research  work:  a  number  of  extensions  and
  1088. optimizations  can be attempted using more sophisticated caching strategies and
  1089. heuristics in servicing fault requests.
  1090.  
  1091.   Besides  final  user  applications  (e.g.  scientific  applications,   window
  1092. managers,  etc.)  there  are a number of operating system utilities that can be
  1093. built using shared memory, knowing that it is now a resource that is  available
  1094. network-wise.    I/O  between  processes  can  be  modeled  as  the transfer of
  1095. ownership of some shared memory buffer.  In this way, a process (the  producer)
  1096. can  allocate  a  buffer,  fill it with data, and then notify the other process
  1097. (consumer) that the buffer is available by enqueuing  it  in,  for  example,  a
  1098. circular  queue.    A  good  case  in  point  is  implementation of the Streams
  1099. abstraction  at  the  user  level.    Supporting  distributed  databases   with
  1100. distributed  shared  memory  also  becomes  more  simple.  An example of how to
  1101. structure a file system using the external pager facility  was  illustrated  in
  1102.  [13],  and  the  Camelot  system [11] uses the facility to provide distributed
  1103. atomic transactions.  Finally, all parallel  languages  that  assume  a  shared
  1104. memory  model  will port easily on a distributed shared memory system, although
  1105. they will require some tuning to obtain the best performance.
  1106.  
  1107.  
  1108.  
  1109. Acknowledgements
  1110.   We would like to thank David Black and Roberto Bisiani for  their  invaluable
  1111. help in reviewing earlier drafts of this paper.
  1112.  
  1113.  
  1114.  
  1115. References
  1116.  
  1117.  
  1118. [1]   Arnould, E. A., Bitz, F. J., Cooper, E. C., Kung, H. T., Sansom, R. D.,
  1119.       Steenkiste, P. A.
  1120.       The Design of Nectar: A Network Backplane for Heterogeneous
  1121.          Multicomputers.
  1122.       April, 1989.
  1123.       To appear in the Proceedings of the Third International Conference on
  1124.          Architectural Support for Programming Languages and Operating Systems
  1125.          (ASPLOS-III).
  1126.  
  1127.  
  1128. [2]   Bisiani, R. and Forin, A.
  1129.       Multilanguage Parallel Programming of Heterogeneous Machines.
  1130.       IEEE Transactions on Computers , August, 1988.
  1131.  
  1132.  
  1133. [3]   Cheriton, D.
  1134.       Unified Management of Memory and File Caching Using the V Virtual Memory
  1135.          System.
  1136.       Tech. Report STAN-CS-88-1192, Stanford University, Computer Science
  1137.          Department, 1988.
  1138.  
  1139.  
  1140. [4]   Fleisch, B. D.
  1141.       Distributed Shared Memory in a Loosely Coupled Distributed System.
  1142.       In Compcon Spring 1988.  IEEE, San Francisco, CA, February, 1988.
  1143.  
  1144.  
  1145. [5]   Forin, A., Bisiani, R., Correrini, F.
  1146.       Parallel Processing with Agora.
  1147.       Tech. Report CMU-CS-87-183, Carnegie-Mellon University, Computer Science
  1148.          Department, December, 1987.
  1149.  
  1150.  
  1151. [6]   Johnson, D.B.
  1152.       Efficient Algorithms For Shortest Path Is Sparse Networks.
  1153.       JACM 24(1):1-13, January, 1977.
  1154.  
  1155.  
  1156. [7]   Kai Li.
  1157.       Shared Virtual Memory on Loosely Coupled Multiprocessors.
  1158.       PhD thesis, Yale, September, 1986.
  1159.  
  1160.  
  1161. [8]   McDonald, J.
  1162.       A Direct Particle Simulation Method for Hypersonic Rarified Flow on a
  1163.          Shared Memory Multiprocessor.
  1164.       Final Project Report CS411, Stanford University, Computer Science
  1165.          Department, March, 1988.
  1166.  
  1167.  
  1168. [9]   Rose, J..
  1169.       LocusRoute: A Parallel Global Router for Standard Cells.
  1170.       In Conf. on Design Automation, pages 189-195.  June, 1988.
  1171.  
  1172.  
  1173. [10]  Sobek, S., Azam, M., Browne, J.C.
  1174.       Architectural and Language Independent Parallel Programming:  A
  1175.          Feasibility Demonstration.
  1176.       In International Conference on Parallel Programming.  IEEE, Chicago,
  1177.          August, 1988.
  1178.  
  1179.  
  1180. [11]  Spector, A.
  1181.       Distributed Transaction Processing and the Camelot System.
  1182.       Distributed Operating Systems: Theory and Practice.
  1183.       Springer-Verlag., 1987.
  1184.  
  1185.  
  1186. [12]  Steenkiste, P.
  1187.       Tags and Type Checking in LISP: Hardware and Software Approaches.
  1188.       In Second Intl. Conference on Architectural Support for Programming
  1189.          Languages and Operating Systems ASPLOS-II.  ACM-SIGPLAN, Palo Alto,
  1190.          CA, October, 1987.
  1191.  
  1192.  
  1193. [13]  Young, M., Tevenian, A., Rashid, R., Golub, D., Eppinger, J., Chew, J.,
  1194.       Bolosky, W., Black, D., Baron, R.
  1195.       The Duality of Memory and Communication in the Implementation of a
  1196.          Multiprocessor Operating System.
  1197.       In 11th Symposium on Operating Systems Principles.  ACM, November, 1987.
  1198.  
  1199.  
  1200. [14]  Young, M.
  1201.       Exporting a User Interface to Memory Management from a Communication-
  1202.          Oriented Operating System.
  1203.       PhD thesis, Carnegie-Mellon University, 1989.
  1204.       In preparation.
  1205.  
  1206.  
  1207.  
  1208.                                Table of Contents
  1209.    1. Introduction                                                            1
  1210.    2. Shared Memory Within a Machine                                          1
  1211.    3. A Simple Algorithm                                                      1
  1212.        3.1. Multiple Page Sizes                                               2
  1213.        3.2. Heterogeneous Processors                                          2
  1214.    4. A Distributed Algorithm                                                 3
  1215.        4.1. Example                                                           4
  1216.    5. Fault Tolerance                                                         4
  1217.    6. Related Work                                                            4
  1218.    7. Performance Evaluation                                                  4
  1219.        7.1. Basic Costs                                                       4
  1220.        7.2. Costs of The Algorithms                                           4
  1221.        7.3. Application Programs                                              5
  1222.    8. Conclusions                                                             6
  1223.  
  1224. Acknowledgements                                                              7
  1225.  
  1226. References                                                                    8
  1227.  
  1228.  
  1229.  
  1230.                                 List of Figures
  1231.    Figure 1:   Summary Of The External Pager Interface                        1
  1232.    Figure 2:   Handling of Read Faults                                        2
  1233.    Figure 3:   Handling of Write Faults                                       2
  1234.    Figure 4:   Handling of Pageout Requests                                   2
  1235.    Figure 5:   A Typed malloc()                                               3
  1236.    Figure 6:   Modifications to the Distributed Scheduler to Implement        3
  1237.                Forwarding of Page Faults
  1238.    Figure 7:   Additions to the Distributed Scheduler to Implement            4
  1239.                Forwarding of Page Faults
  1240.    Figure 8:   Steady State Behavior for a N-Party Write Hotspot              4
  1241.  
  1242.  
  1243.  
  1244.                                 List of Tables
  1245.    Table 1:   Costs Of The Server.                                            5
  1246.    Table 2:   Overhead of Data Translations (in milliseconds per 4kbytes).    5
  1247.    Table 3:   Execution Times For Some Application Programs                   5
  1248.