N-QueensをPARDSで並列化

できた。昨日C++版N-Queensを若干変更。
変更点は、「答えリスト」だけを特別扱いして、共有メモリ上に確保。あと、答えリストの中身は配列で表現することで、共有メモリ確保のオーバヘッドを減らした。
また、やたらプロセスを生成してもオーバヘッドが増えるので、関数"qu"の1段目だけを"qudist"という別関数にして、この関数の中でだけSPAWNすることにした。関数中1回しかSPAWNしないので、サイズが13だと、13プロセスしか生成しない。ので、負荷の不均衡がおこる可能性がある。

サイズ = 13、Macbook (Core 2 Duo)で、

  • 元のプログラム:5.7秒
  • プログラム変更、SPAWNせず:6秒
  • プログラム変更、SPAWN(2台):3.2秒

なので、まずまず、というところか?

PARDSを利用したプログラムスタイルとして、

  • 並列化前の関数で返り値だった値をSync変数にくるんで引数として与える
  • (上述の返り値がポインタである場合は特に)Sync変数に対するread/writeのみで通信するのではなく、共有メモリを用いて通信する

というのがパターン、かなあ?

プログラムは以下の通り:

#include "Sync.h"

template <class T> class List{
public:
  T value;
  List<T>* next;
};

class AnsList{
public:
  int ans[32];
  AnsList* next;
  static void* operator new(size_t size);
  static void operator delete(void* obj);
};

void* AnsList::operator new(size_t size)
{
  void* ptr = pards_shmalloc(size);
  return ptr;
}

void AnsList::operator delete(void* obj)
{
  pards_shmfree(obj);
  return;
}

List<int>* generate(int n){
  if(n > 0) {
    List<int>* c = new List<int>;
    c->value = n; c->next = generate(n-1);
    return c;
  } else {
    return 0;
  }
}

template <class T> List<T>* append(List<T>* a, List<T>* b){
  if(a != 0){
    T h = a->value;
    List<T>* t = a->next;
    List<T>* res = new List<T>;
    res->value = h;
    res->next = append(t,b);
    return res;
  } else {
    return b;
  }
}

AnsList* appendAnsList(AnsList* a, AnsList* b){
  if(a == 0) return b;
  else {
    AnsList* tmp = a;
    while(tmp->next != 0) tmp = tmp->next;
    tmp->next = b;
    return a;
  }
}

void ListToAnsList(List<int>* l, AnsList* a){
  List<int>* crnt = l;
  for(int i = 0; crnt != 0 ;i++){
    a->ans[i] = crnt->value;
    crnt = crnt->next;
  }
}

void qudist(List<int>* plu, List<int>* ls, List<int>* lp, Sync<AnsList*> ans){
  if(plu == 0) {
    if(ls == 0) {
      AnsList* res = new AnsList;
      ListToAnsList(lp,res);
      res->next = 0;
      ans.write(res);
      return;
    } else {
      ans.write(0);
      return;
    }
  } else {
    AnsList* check(int, int, List<int>*, List<int>*, List<int>*);

    List<int>*  lu = plu->next;
    int p = plu->value;
    List<int>* lr = append<int>(lu, ls);

    List<int>* nls = new List<int>;
    nls->value = p;
    nls->next = ls;

    Sync<AnsList*> syncans2;
    SPAWN(qudist(lu, nls, lp, syncans2));
    AnsList* ans1 = check(p, 1, lr, lp, lp);

    AnsList* ans2 = syncans2.read();
    ans.write(appendAnsList(ans1, ans2));
  }
}

AnsList* qu(List<int>* plu, List<int>* ls, List<int>* lp){
  if(plu == 0) {
    if(ls == 0) {
      AnsList* res = new AnsList;
      ListToAnsList(lp,res);
      res->next = 0;
      return res;
    } else {
      return 0;
    }
  } else {
    AnsList* check(int, int, List<int>*, List<int>*, List<int>*);

    List<int>*  lu = plu->next;
    int p = plu->value;
    List<int>* lr = append<int>(lu, ls);
    AnsList* ans1 = check(p, 1, lr, lp, lp);
    List<int>* nls = new List<int>;
    nls->value = p;
    nls->next = ls;
    AnsList* ans2 = qu(lu, nls, lp);
    return appendAnsList(ans1, ans2);
  }
}

AnsList* check(int p, int d, List<int>* l, List<int>* qlp0, List<int>* lp){
  if(qlp0 == 0) {
    List<int>* plp = new List<int>;
    plp->value = p;
    plp->next = lp;
    return qu(l,0,plp);
  } else {
    int q = qlp0->value;
    if(q + d == p || q - d == p)
      return 0;
    else{
      List<int>* lp0 = qlp0->next;
      return check(p,d+1,l,lp0,lp);
    }
  }
}

int main(int argc, char* argv[])
{
  if(argc < 2) {
    printf("qu NUM\n");
    exit(0);
  }

  int num = atoi(argv[1]);

  pards_init();

  List<int>* gen = generate(num);
  Sync<AnsList*> ans;
  qudist(gen,0,0,ans);

  AnsList* res = ans.read();
  
  int i = 0;
  for(; res != 0; res = res->next, i++){
    /*    for(int i = 0; i < num; i++)
	  printf("%d ", res->ans[i]);
	  printf("\n"); */
  }
  printf("num = %d\n",i);

  pards_finalize();
}