No Such Blog or Diary

«Prev || 1 | 2 | 3 | 4 |...| 12 | 13 | 14 || Next»

istringstream + binary_iarchive ではまってみる

MPIで可変サイズのデータを通信するために boost::serialization を使おうとして,受信部に下のようなコードを書いた.繰り返しサイズ不定なデータを受信するので,バッファはvector<char>にしてサイズを楽に変えられるようにしている.そして,受信したデータの復元のためにバッファをistringstreamに包んでbinary_iarchiveに投げている.細かいことを気にしないと正しく動くように見える.コンパイルも通るし.

std::vector<char> buf;
for (int stage = 1; stage < procs; stage <<= 1) {
  // snip
  unsigned int s = 0;
  MPI_Recv(&s, 1, MPI_INT, target, TAG1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  if(s > buf.size()) buf.resize(s);
  MPI_Recv(&(*buf.begin()), s, MPI_BYTE, target, TAG2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  std::istringstream  is(&(*buf.begin()), std::ios_base::in | std::ios_base::binary);
  boost::archive::binary_iarchive bai(is);
  // snip
}

が,これを動かすとbinary_iarchiveのコンストラクタでbad_allocを食らって落ちる.なぜ?

しばらく頭も回らず原因が分からなかったけどよく考えるとistringstreamのeofが判定できないんじゃないかと気づく.そのせいでbinary_iarchiveがめちゃくちゃな量を読みに行くかなんかしてメモリ不足で落ちているのではないかと.

ということで,下のように書き換えたら動いた.でもstring作る分だけ無駄だよなぁ.どうすりゃいいんだろう?

  std::string str(&(*buf.begin()), s);
  std::istringstream  is(str, std::ios_base::in | std::ios_base::binary);
  boost::archive::binary_iarchive bai(is);

GCCのOpenMPでboolの和をとると…

C++のOpenMPでは最大値が取れないと言ったら,「boolなら+で最大値取れるじゃん,嘘言うな.」と返ってきた.意味があるかどうかはさておき,確かに出来ることなので実際にやってみた.

ソースは次のとおり.bsにbool値を適当に入れて,それの和をbool変数rに得る計算をreduction(+:r)で並列化する.最後に結果の出力として r を吐き出す.コンパイルはg++に-fopenmpo付けてやった.

#include<iostream>
#include<vector>
 
int k = 2;
int main(int argc, char *argv[])
{
  int n = 10000000;
  std::vector<bool> bs(n);
  for(int i = 0; i < n; i++) {
    bs[i] = i % k;
  }
  bool r = false;
#pragma omp parallel for reduction(+:r)
  for(int i = 0; i < n; i++) {
    r += bs[i];
  }
  int res = r;
  std::cout << res << std::endl;
}

結果:逐次で動かすと1が返ってくるが,OpenMPで並列化したら8コア(スレッド)で8が返ってきた.

普通に考えればrはboolなのでintへの変換時に0か1にしかならないと思うのだけど…,なぜに8? そもそも逐次と結果違うってのもどうかと.なんとなくreductionの結果をとるときにrの型がboolでなくintとかにされている気がする.ま,詳しくはまた今度調べよう.

最大値を取りたいだけだけど

コンテナxに入ってる正整数(int)の最大値をとるのに

std::accumulate(x.begin(), x.end(), 0, (const int& (*)(const int&,const int&))std::max<int>)

とか書くのはバカなのかなぁ.素直にstd::max_element使えと言われそう.イテレータが返ってくることに注意する必要があるけど.

libstdc++ parallel mode のバグ

コンパイル時に -D_GLIBCXX_PARALLEL つけても,プログラム中で __gnu_parallel::transform とか明示的に呼んでも,ちっとも並列化してくれない.最後の引数に __gnu_parallel::parallel_balanced とか指定するとやっと並列で動いてくれる.こんな動作が意図したものなのかどうかを問い合わせたらやっぱりバグだそうで.

とりあえず問い合わせ時に書いておいた対処法でパッチ作ってもらえたのでそれを当てつつ様子を見る.

テンプレート関数の再宣言時のデフォルト値は標準ではない?

テンプレート関数のデフォルト引数を再宣言時に書くとg++でコンパイルがこける.例えば,下のプログラムのテンプレート関数funcは再宣言時(最初の宣言はプロトタイプ宣言で,関数の定義が再宣言)にデフォルト引数を入れているけど,g++は最初のプロトタイプ宣言にデフォルト引数がなかったのでfuncにはデフォルト引数がないと判断するらしい.結果としてmain関数内のfuncの呼び出しがこける.テンプレート関数でなくて普通の関数だと問題ないのでhogeの呼び出しは通る.少なくとも,g++ の 3.4.4, 4.2.3, 4.3.1, 4.3.2, 4.4.0 で同じ現象.

#include<iostream>
using namespace std;
 
template<typename B>
void func(const B&, const char&, int k);
 
template<typename B>
void func(const B&, const char&, int k = 0)
{
  cout << "func" << endl;
}
 
void hoge(const int&, const char&, int k);
void hoge(const int&, const char&, int k = 0)
{
  cout << "hoge" << endl;
}
 
int main(int argc, char *argv[])
{
  int n = 1;
  char m = 0;
  hoge(n,m); //ok
  func(n,m); //ng
  return 0;
}

で,他のコンパオラで試したら以下のとおり.icc (Intel C Compiler 10.1.018) は warning #845: specifying a default argument when redeclaring an unreferenced function template is nonstandard と言って警告を出すが,コンパイルは通る.lc (Visual Studio .NET 2005 のコンパイラ) は何も言わずにコンパイルできる.bcc (Borland C++ 5.82) も何も言わずにコンパイル可能.

とりあえずgccどうにかしてほしい.

とりあえず

"libgomp.spec: No such file or directory" のエラーは 4.3.2 でも出たと.しょうがないので svn のリポジトリから最新版を落としてやったらエラーでなかった.はぁ.

«Prev || 1 | 2 | 3 | 4 |...| 12 | 13 | 14 || Next»
Search
Feeds

Page Top