فرز الدمج المتزامن في الذاكرة المشتركة

نظرا لرقم 'n' وأرقام n، قم بفرز الأرقام باستخدام متزامن دمج الفرز. (تلميح: حاول استخدام مكالمات نظام shmget shmat).
الجزء الأول: الخوارزمية (كيف؟)  
قم بإجراء عمليتين فرعيتين بشكل متكرر، واحدة للنصف الأيسر والأخرى للنصف الأيمن. إذا كان عدد العناصر في المصفوفة لعملية ما أقل من 5، فقم بإجراء فرز الإدراج . يقوم والد الطفلين بعد ذلك بدمج النتيجة وإعادتها إلى الوالد وهكذا. ولكن كيف تجعلها متزامنة؟
الجزء الثاني: المنطق (لماذا؟)  
الجزء المهم من حل هذه المشكلة ليس خوارزميًا، بل شرح مفاهيم نظام التشغيل والنواة. 
لتحقيق الفرز المتزامن، نحتاج إلى طريقة لجعل عمليتين تعملان على نفس المصفوفة في نفس الوقت. لتسهيل الأمور، يوفر Linux الكثير من مكالمات النظام عبر نقاط نهاية واجهة برمجة التطبيقات (API) البسيطة. اثنان منهم شمجيت() (لتخصيص الذاكرة المشتركة) و شمات() (لعمليات الذاكرة المشتركة). نقوم بإنشاء مساحة ذاكرة مشتركة بين العملية الفرعية التي نتفرع عنها. يتم تقسيم كل جزء إلى فرعين يسار ويمين ويتم فرز الجزء المثير للاهتمام حيث أنهما يعملان بشكل متزامن! يطلب shmget() من النواة تخصيص ملف صفحة مشتركة لكلا العمليتين.
لماذا لا تعمل الشوكة التقليدية ()؟  
تكمن الإجابة في ما تفعله fork() بالفعل. من الوثائق، يقوم "fork() بإنشاء عملية جديدة عن طريق تكرار عملية الاستدعاء". تعمل العملية الفرعية والعملية الأصلية في مساحات ذاكرة منفصلة. في وقت fork()، تحتوي كلتا مساحتي الذاكرة على نفس المحتوى. تكتب الذاكرة تغييرات واصف الملف (fd) وما إلى ذلك التي يتم إجراؤها بواسطة إحدى العمليات ولا تؤثر على الأخرى. وبالتالي نحن بحاجة إلى شريحة الذاكرة المشتركة.
 

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   

تعقيد الوقت :O(N log N )

المساحة المساعدة:O(N)


تحسينات الأداء؟  
حاول تحديد وقت الكود ومقارنة أدائه بالكود التسلسلي التقليدي. ستندهش عندما تعرف أن أداء الفرز المتسلسل أفضل! 
عندما نقول وصول الطفل الأيسر إلى المصفوفة اليسرى، يتم تحميل المصفوفة في ذاكرة التخزين المؤقت للمعالج. الآن، عند الوصول إلى المصفوفة اليمنى (بسبب الوصول المتزامن)، هناك ذاكرة تخزين مؤقت مفقودة نظرًا لأن ذاكرة التخزين المؤقت مليئة بالجزء الأيسر ثم يتم نسخ الجزء الأيمن إلى ذاكرة التخزين المؤقت. تستمر هذه العملية ذهابًا وإيابًا وتؤدي إلى انخفاض الأداء إلى مستوى يؤدي إلى أداء أقل من الكود التسلسلي.
هناك طرق لتقليل أخطاء ذاكرة التخزين المؤقت من خلال التحكم في سير عمل التعليمات البرمجية. ولكن لا يمكن تجنبها تماما!