それでもおかしい並列qsort

segvで落ちる並列qsortについて、どこからともなく神の声が聞こえた。

main()のpはstackにおかないでmallocする、ret1とret2を0以外で初期化する、if (...||...) free(pnext); の行を削除、myqsort()の最後でちゃんとreturn NULL;する、とかすれば多分動きそうです。

なるほど。特にret1、ret2を0以外で初期化していなかったのがまずそう。スレッド生成が発生しないケースで、たまたまretの値が0となってjoinしようとすれば落ちておかしくない。

あと、mainのスタックにデータを置いていたのは、何となくmain関数は特別なグローバルな存在という意識があったことからくる間違いだ。mainだろうが、なんだろうが、スレッドはすべて対等。ということは、すべてのスレッドからアクセスするデータはヒープにおかないとダメだ。

以下のように書き換えた。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include "psort.h"

#define SWAP(type, x, y) do {type tmp = x; x = y; y = tmp;}while(0)

void *myqsort(void *p) {

  pthread_t thread1, thread2; 
  int ret1, ret2;
  ret1 = ret2 = 1;

  partition *part, *pnext1, *pnext2;

  part = (partition *)p;

  int left = part->begin;
  int right = part->end;

  if (left >= right) {
    pthread_exit(NULL);
  }

  int pivot = part->ary[(left + right) / 2];
  
  while (1) {
    while(part->ary[left] < pivot) left++;
    while(part->ary[right] > pivot) right--;
    if (left >= right) break;

    SWAP(int, part->ary[left], part->ary[right]);
    left++; right--;
  }

  if (left - part->begin >= 2) {
    pnext1 = (partition *)malloc(sizeof(partition));
    pnext1->ary = part->ary;
    pnext1->begin = part->begin;
    pnext1->end = left - 1;
    ret1 = pthread_create(&thread1, NULL, myqsort, (void *)pnext1);
  }
  if (part->end - right >= 2) {
    pnext2 = (partition *)malloc(sizeof(partition));
    pnext2->ary = part->ary;
    pnext2->begin = right + 1;
    pnext2->end = part->end;
    ret2 = pthread_create(&thread2, NULL, myqsort, (void *)pnext2);
  }
  if (ret1 == 0) {
    pthread_join(thread1, NULL);
  }
  if (ret2 == 0) {
    pthread_join(thread2, NULL);
  }
  return NULL;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#include "psort.h"

int main (int argc, char *argv[]) {
  int size, i;
  int *x;
  partition *p;

  if (argc < 1) exit(1);
  size = atoi(argv[1]);

  p = (partition *)malloc(sizeof(partition));
  x = (int *)malloc(sizeof(int)*size);

  srand(time(NULL));
  for (i = 0; i < size; i++) {
    x[i] = rand() % size;
  }

  show_ary(x, size);
  p->ary = x;
  p->begin = 0;
  p->end = size - 1;
  myqsort(p);
  //  sleep(1);
  printf("--sort--\n");
  show_ary(x, size);
}

これでsegvで落ちなくなった。いい感じ、いい感じと思ったのもつかの間、少し大きな並びを突っ込むとどうも並びきってない箇所が現れる。要素数300だとキレイに並ぶけど、要素数3万だと、どうもおかしなところがあっちこっちにある。

グラフにしてみた。もとのデータは乱数なので、ややでこぼこしながらも直線的に並ぶのが正解。3万個のときには明らかに一部のデータがおかしい。


しかし、グラフがちょっとおもしろい。これはどういう意味だろうか。sleep(1)で寝かせてみても、ちゃんとソートされない領域が残る。