Истовремено сортирање спајањем у дељеној меморији

Дати број 'н' и а н бројева сортирајте бројеве користећи Истовремено Сортирање спајањем. (Савет: Покушајте да користите схмгет схмат системске позиве).
Део 1: Алгоритам (КАКО?)  
Рекурзивно направите два подређена процеса један за леву половину један за десну половину. Ако је број елемената у низу за процес мањи од 5 извршите а Инсертион Сорт . Родитељ двоје деце затим спаја резултат и враћа се родитељу и тако даље. Али како то учинити истовремено?
Део 2: Логично (ЗАШТО?)  
Важан део решења овог проблема није алгоритамски, већ објашњавање концепата оперативног система и кернела. 
Да бисмо постигли истовремено сортирање, потребан нам је начин да натерамо два процеса да раде на истом низу у исто време. Да би олакшао ствари, Линук пружа много системских позива преко једноставних АПИ крајњих тачака. Два од њих су схмгет() (за доделу заједничке меморије) и схмат() (за операције заједничке меморије). Креирамо заједнички меморијски простор између подређеног процеса који раздвајамо. Сваки сегмент је подељен на лево и десно дете које је сортирано занимљивим делом јер раде истовремено! схмгет() захтева од кернела да додели а дељена страница за оба процеса.
Зашто традиционални форк() не ради?  
Одговор лежи у томе шта форк() заправо ради. Из документације 'форк() креира нови процес дуплирањем процеса позивања'. Подређени процес и родитељски процес се покрећу у одвојеним меморијским просторима. У време форк() оба меморијска простора имају исти садржај. Меморија уписује фајл-дескриптор(фд) промене итд. које је извршио један од процеса не утичу на други. Стога нам је потребан сегмент заједничке меморије.
 

CPP
   #include          #include         #include         #include         #include         #include         #include         #include         void     insertionSort  (  int     arr  []     int     n  );   void     merge  (  int     a  []     int     l1       int     h1       int     h2  );   void     mergeSort  (  int     a  []     int     l       int     h  )   {      int     i       len     =     (  h     -     l     +     1  );      // Using insertion sort for small sized array      if     (  len      <=     5  )      {      insertionSort  (  a     +     l       len  );      return  ;      }      pid_t     lpid       rpid  ;      lpid     =     fork  ();      if     (  lpid      <     0  )      {      // Lchild proc not created      perror  (  'Left Child Proc. not created  n  '  );      _exit  (  -1  );      }      else     if     (  lpid     ==     0  )      {      mergeSort  (  a       l       l     +     len     /     2     -     1  );      _exit  (  0  );      }      else      {      rpid     =     fork  ();      if     (  rpid      <     0  )      {      // Rchild proc not created      perror  (  'Right Child Proc. not created  n  '  );      _exit  (  -1  );      }      else     if     (  rpid     ==     0  )      {      mergeSort  (  a       l     +     len     /     2       h  );      _exit  (  0  );      }      }      int     status  ;      // Wait for child processes to finish      waitpid  (  lpid       &  status       0  );      waitpid  (  rpid       &  status       0  );      // Merge the sorted subarrays      merge  (  a       l       l     +     len     /     2     -     1       h  );   }   /* Function to sort an array using insertion sort*/   void     insertionSort  (  int     arr  []     int     n  )   {      int     i       key       j  ;      for     (  i     =     1  ;     i      <     n  ;     i  ++  )      {      key     =     arr  [  i  ];      j     =     i     -     1  ;      /* Move elements of arr[0..i-1] that are    greater than key to one position ahead    of their current position */      while     (  j     >=     0     &&     arr  [  j  ]     >     key  )      {      arr  [  j     +     1  ]     =     arr  [  j  ];      j     =     j     -     1  ;      }      arr  [  j     +     1  ]     =     key  ;      }   }   // Method to merge sorted subarrays   void     merge  (  int     a  []     int     l1       int     h1       int     h2  )   {      // We can directly copy the sorted elements      // in the final array no need for a temporary      // sorted array.      int     count     =     h2     -     l1     +     1  ;      int     sorted  [  count  ];      int     i     =     l1       k     =     h1     +     1       m     =     0  ;      while     (  i      <=     h1     &&     k      <=     h2  )      {      if     (  a  [  i  ]      <     a  [  k  ])      sorted  [  m  ++  ]     =     a  [  i  ++  ];      else     if     (  a  [  k  ]      <     a  [  i  ])      sorted  [  m  ++  ]     =     a  [  k  ++  ];      else     if     (  a  [  i  ]     ==     a  [  k  ])      {      sorted  [  m  ++  ]     =     a  [  i  ++  ];      sorted  [  m  ++  ]     =     a  [  k  ++  ];      }      }      while     (  i      <=     h1  )      sorted  [  m  ++  ]     =     a  [  i  ++  ];      while     (  k      <=     h2  )      sorted  [  m  ++  ]     =     a  [  k  ++  ];      int     arr_count     =     l1  ;      for     (  i     =     0  ;     i      <     count  ;     i  ++       l1  ++  )      a  [  l1  ]     =     sorted  [  i  ];   }   // To check if array is actually sorted or not   void     isSorted  (  int     arr  []     int     len  )   {      if     (  len     ==     1  )      {      std  ::  cout      < <     'Sorting Done Successfully'      < <     std  ::  endl  ;      return  ;      }      int     i  ;      for     (  i     =     1  ;     i      <     len  ;     i  ++  )      {      if     (  arr  [  i  ]      <     arr  [  i     -     1  ])      {      std  ::  cout      < <     'Sorting Not Done'      < <     std  ::  endl  ;      return  ;      }      }      std  ::  cout      < <     'Sorting Done Successfully'      < <     std  ::  endl  ;      return  ;   }   // To fill random values in array for testing   // purpose   void     fillData  (  int     a  []     int     len  )   {      // Create random arrays      int     i  ;      for     (  i     =     0  ;     i      <     len  ;     i  ++  )      a  [  i  ]     =     rand  ();      return  ;   }   // Driver code   int     main  ()   {      int     shmid  ;      key_t     key     =     IPC_PRIVATE  ;      int     *  shm_array  ;      int     length     =     128  ;      // Calculate segment length      size_t     SHM_SIZE     =     sizeof  (  int  )     *     length  ;      // Create the segment.      if     ((  shmid     =     shmget  (  key       SHM_SIZE       IPC_CREAT     |     0666  ))      <     0  )      {      perror  (  'shmget'  );      _exit  (  1  );      }      // Now we attach the segment to our data space.      if     ((  shm_array     =     (  int     *  )  shmat  (  shmid       NULL       0  ))     ==     (  int     *  )  -1  )      {      perror  (  'shmat'  );      _exit  (  1  );      }      // Create a random array of given length      srand  (  time  (  NULL  ));      fillData  (  shm_array       length  );      // Sort the created array      mergeSort  (  shm_array       0       length     -     1  );      // Check if array is sorted or not      isSorted  (  shm_array       length  );      /* Detach from the shared memory now that we are    done using it. */      if     (  shmdt  (  shm_array  )     ==     -1  )      {      perror  (  'shmdt'  );      _exit  (  1  );      }      /* Delete the shared memory segment. */      if     (  shmctl  (  shmid       IPC_RMID       NULL  )     ==     -1  )      {      perror  (  'shmctl'  );      _exit  (  1  );      }      return     0  ;   }   
Java
   import     java.util.Arrays  ;   import     java.util.Random  ;   import     java.util.concurrent.ForkJoinPool  ;   import     java.util.concurrent.RecursiveAction  ;   public     class   ConcurrentMergeSort     {      // Method to merge sorted subarrays      private     static     void     merge  (  int  []     a       int     low       int     mid       int     high  )     {      int  []     temp     =     new     int  [  high     -     low     +     1  ]  ;      int     i     =     low       j     =     mid     +     1       k     =     0  ;      while     (  i      <=     mid     &&     j      <=     high  )     {      if     (  a  [  i  ]      <=     a  [  j  ]  )     {      temp  [  k  ++]     =     a  [  i  ++]  ;      }     else     {      temp  [  k  ++]     =     a  [  j  ++]  ;      }      }      while     (  i      <=     mid  )     {      temp  [  k  ++]     =     a  [  i  ++]  ;      }      while     (  j      <=     high  )     {      temp  [  k  ++]     =     a  [  j  ++]  ;      }      System  .  arraycopy  (  temp       0       a       low       temp  .  length  );      }      // RecursiveAction for fork/join framework      static     class   SortTask     extends     RecursiveAction     {      private     final     int  []     a  ;      private     final     int     low       high  ;      SortTask  (  int  []     a       int     low       int     high  )     {      this  .  a     =     a  ;      this  .  low     =     low  ;      this  .  high     =     high  ;      }      @Override      protected     void     compute  ()     {      if     (  high     -     low      <=     5  )     {      Arrays  .  sort  (  a       low       high     +     1  );      }     else     {      int     mid     =     low     +     (  high     -     low  )     /     2  ;      invokeAll  (  new     SortTask  (  a       low       mid  )     new     SortTask  (  a       mid     +     1       high  ));      merge  (  a       low       mid       high  );      }      }      }      // Method to check if array is sorted      private     static     boolean     isSorted  (  int  []     a  )     {      for     (  int     i     =     0  ;     i      <     a  .  length     -     1  ;     i  ++  )     {      if     (  a  [  i  ]     >     a  [  i     +     1  ]  )     {      return     false  ;      }      }      return     true  ;      }      // Method to fill array with random numbers      private     static     void     fillData  (  int  []     a  )     {      Random     rand     =     new     Random  ();      for     (  int     i     =     0  ;     i      <     a  .  length  ;     i  ++  )     {      a  [  i  ]     =     rand  .  nextInt  ();      }      }      public     static     void     main  (  String  []     args  )     {      int     length     =     128  ;      int  []     a     =     new     int  [  length  ]  ;      fillData  (  a  );      ForkJoinPool     pool     =     new     ForkJoinPool  ();      pool  .  invoke  (  new     SortTask  (  a       0       a  .  length     -     1  ));      if     (  isSorted  (  a  ))     {      System  .  out  .  println  (  'Sorting Done Successfully'  );      }     else     {      System  .  out  .  println  (  'Sorting Not Done'  );      }      }   }   
Python3
   import   numpy   as   np   import   multiprocessing   as   mp   import   time   def   insertion_sort  (  arr  ):   n   =   len  (  arr  )   for   i   in   range  (  1     n  ):   key   =   arr  [  i  ]   j   =   i   -   1   while   j   >=   0   and   arr  [  j  ]   >   key  :   arr  [  j   +   1  ]   =   arr  [  j  ]   j   -=   1   arr  [  j   +   1  ]   =   key   def   merge  (  arr     l     mid     r  ):   n1   =   mid   -   l   +   1   n2   =   r   -   mid   L   =   arr  [  l  :  l   +   n1  ]  .  copy  ()   R   =   arr  [  mid   +   1  :  mid   +   1   +   n2  ]  .  copy  ()   i   =   j   =   0   k   =   l   while   i    <   n1   and   j    <   n2  :   if   L  [  i  ]    <=   R  [  j  ]:   arr  [  k  ]   =   L  [  i  ]   i   +=   1   else  :   arr  [  k  ]   =   R  [  j  ]   j   +=   1   k   +=   1   while   i    <   n1  :   arr  [  k  ]   =   L  [  i  ]   i   +=   1   k   +=   1   while   j    <   n2  :   arr  [  k  ]   =   R  [  j  ]   j   +=   1   k   +=   1   def   merge_sort  (  arr     l     r  ):   if   l    <   r  :   if   r   -   l   +   1    <=   5  :   insertion_sort  (  arr  )   else  :   mid   =   (  l   +   r  )   //   2   p1   =   mp  .  Process  (  target  =  merge_sort     args  =  (  arr     l     mid  ))   p2   =   mp  .  Process  (  target  =  merge_sort     args  =  (  arr     mid   +   1     r  ))   p1  .  start  ()   p2  .  start  ()   p1  .  join  ()   p2  .  join  ()   merge  (  arr     l     mid     r  )   def   is_sorted  (  arr  ):   for   i   in   range  (  1     len  (  arr  )):   if   arr  [  i  ]    <   arr  [  i   -   1  ]:   return   False   return   True   def   fill_data  (  arr  ):   np  .  random  .  seed  (  0  )   arr  [:]   =   np  .  random  .  randint  (  0     1000     size  =  len  (  arr  ))   if   __name__   ==   '__main__'  :   length   =   128   shm_array   =   mp  .  Array  (  'i'     length  )   fill_data  (  shm_array  )   start_time   =   time  .  time  ()   merge_sort  (  shm_array     0     length   -   1  )   end_time   =   time  .  time  ()   if   is_sorted  (  shm_array  ):   print  (  'Sorting Done Successfully'  )   else  :   print  (  'Sorting Not Done'  )   print  (  'Time taken:'     end_time   -   start_time  )   
JavaScript
   // Importing required modules   const     {     Worker       isMainThread       parentPort       workerData     }     =     require  (  'worker_threads'  );   // Function to merge sorted subarrays   function     merge  (  a       low       mid       high  )     {      let     temp     =     new     Array  (  high     -     low     +     1  );      let     i     =     low       j     =     mid     +     1       k     =     0  ;      while     (  i      <=     mid     &&     j      <=     high  )     {      if     (  a  [  i  ]      <=     a  [  j  ])     {      temp  [  k  ++  ]     =     a  [  i  ++  ];      }     else     {      temp  [  k  ++  ]     =     a  [  j  ++  ];      }      }      while     (  i      <=     mid  )     {      temp  [  k  ++  ]     =     a  [  i  ++  ];      }      while     (  j      <=     high  )     {      temp  [  k  ++  ]     =     a  [  j  ++  ];      }      for     (  let     p     =     0  ;     p      <     temp  .  length  ;     p  ++  )     {      a  [  low     +     p  ]     =     temp  [  p  ];      }   }   // Function to check if array is sorted   function     isSorted  (  a  )     {      for     (  let     i     =     0  ;     i      <     a  .  length     -     1  ;     i  ++  )     {      if     (  a  [  i  ]     >     a  [  i     +     1  ])     {      return     false  ;      }      }      return     true  ;   }   // Function to fill array with random numbers   function     fillData  (  a  )     {      for     (  let     i     =     0  ;     i      <     a  .  length  ;     i  ++  )     {      a  [  i  ]     =     Math  .  floor  (  Math  .  random  ()     *     1000  );      }   }   // Function to sort the array using merge sort   function     sortArray  (  a       low       high  )     {      if     (  high     -     low      <=     5  )     {      a  .  sort  ((  a       b  )     =>     a     -     b  );      }     else     {      let     mid     =     low     +     Math  .  floor  ((  high     -     low  )     /     2  );      sortArray  (  a       low       mid  );      sortArray  (  a       mid     +     1       high  );      merge  (  a       low       mid       high  );      }   }   // Main function   function     main  ()     {      let     length     =     128  ;      let     a     =     new     Array  (  length  );      fillData  (  a  );      sortArray  (  a       0       a  .  length     -     1  );      if     (  isSorted  (  a  ))     {      console  .  log  (  'Sorting Done Successfully'  );      }     else     {      console  .  log  (  'Sorting Not Done'  );      }   }   main  ();   

Излаз: 
 

 Sorting Done Successfully   

Временска сложеност :О(Н лог Н)

Помоћни простор: О(Н)


Побољшања перформанси?  
Покушајте да одредите време кода и упоредите његове перформансе са традиционалним секвенцијалним кодом. Изненадили бисте се да боље знате перформансе секвенцијалног сортирања! 
Када рецимо лево дете приступи левом низу, низ се учитава у кеш процесора. Сада када се приступи десном низу (због истовремених приступа) долази до промашаја кеш меморије пошто је кеш попуњен левим сегментом, а затим се десни сегмент копира у кеш меморију. Овај процес напред-назад се наставља и деградира перформансе на такав ниво да ради лошије од секвенцијалног кода.
Постоје начини за смањење пропуста у кеш меморији контролисањем тока посла кода. Али они се не могу потпуно избећи!


 


Топ Чланци

Категорија