A mere hateblo

単なるはてブロです。

ODSファイル内のシートのデータが大きくなりすぎた時の対処方法

仕事のデータを作成するのにLibreOffice Calcを利用しています。具体的には数値や文字列などCalcで入力してODSファイルとして保存、ODSファイルの内容をRubyから読み取ってバイナリファイルやC言語のヘッダファイルを作成しています。普段はとくに問題ありませんが、稀にODSファイルに保存したシートのデータが馬鹿でかくなるせいでRubyからデータを読み取る際にえらく時間がかかることがあります。で、問題は解決していませんが、それなりな対処方法がわかったのでココにメモしておきます。

以前ODSファイルに含まれるcontent.xmlの内容を調べたところ、問題発生時にはなぜか1行に含まれるセルの数が実際のデータよりもはるかに多い状態になり、それが全行数で起きているようでした。そのため、延々と空のセルを読むという無駄な処理ばかりしていました。一番大きなシートはだいたい8000行x15列くらいのもので、Ruby 1.9.3-p125とnokogiriを使ってだいたい50秒弱で処理できる規模のものですが、問題が起きると数倍から数十倍時間がかかるようになります。もうこうなると仕事になりません。

問題が発生するキッカケがわからないため、バックアップ目的でODSファイルをこまめにSubversionリポジトリへcommitし、問題が起きたらrevertするという対処を続けてきました。これはこれで問題には対処できますが、それまで入力したものとかが無駄になりがちでした。しかしとうとう、現状は問題なくとも、少しでもセルの内容を変更してODSファイルへ保存するだけで上記問題が発生するようになってしまいました。

今日になってさらに調べたところ、この問題はシート単位で起こることがわかりました。そこでシートを新規作成し、問題のシートの内容を必要なところだけ新規シートへ移し、問題のシートを削除する、という対処をとったところ、上記問題を解消できました。ただし、問題のあるシートを対処したところ別の問題のなかったシートに問題が発生することがありましたので、結局すべてのシートで問題が起きなくなるまで対処を続けました。全10シートのうち3シート対処しましたが、シート数が少ないからいいものを、シート数が多くなると頭抱えそうです。

問題の再現方法がわかればLibreOffice(OpenOfficeも多分同様)へ報告したいところですが、突然起きるのでなんともできないのが歯がゆいです。同じような問題を抱えている人もググった限りでは見つかりませんでしたし、Calc上で編集する分にはなんら問題のないことなので、余計に報告しづらいというか、なんというか。

Cygwinでスタックサイズが足りなくなった

昨日、Cygwinのshがスタックが足りないなどと言い異常終了するようになった。エラーメッセージは記録し忘れたが、以下のリンク先にある「can't commit memory for stack」というのがそれ。
"cant commit memory for stack" error with perl

このエラーは初めて見たので対処方法がわからなく右往左往したが、結局peflagsコマンドを使い、/bin/shのスタックサイズを倍増することで対処できた。

具体的には、まず/bin/shの現在のスタックサイズを確認する:

% peflags -x /bin/sh
/bin/sh: stack reserve size      : 2097152 (0x200000) bytes

次に/bin/shのスタックサイズを倍の数値に設定する:

% peflags -x0x400000 /bin/sh
/bin/sh: stack reserve size      : 4194304 (0x400000) bytes

これにより冒頭の異常終了を回避できた。
正直スタックサイズをこんなに消費する方もどうかと思うが、対処しないと仕事が進まないので仕方なくこのようにした。

hub alias

ここ最近zsh起動時に以下のメッセージが出るようになった:

(eval):1: parse error near `then'

原因は~/.zshrcで以下のように実行したからだった:

eval `hub alias -s zsh`

たしかhub導入時は上記を実行するよう言われてたと記憶しているが、どうやらhubの最新ドキュメントを見ると今は以下のようにするらしい:

eval "$(hub alias -s)"

これでparse errorのメッセージが出なくなった。

FreeBSD portsのdevel/boost-libsが1.45.0のままな件について

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=ports/156253

現状send-prされたものでopenなものはこれだけで、どうもboost 1.46.1へのアップデートを提案したところ関連portで不具合が起き、対処している間にboostのバージョンが上がっていった、と。現在はboostを利用するportの不具合を改善しつつ新しいboostへの対応パッチを用意している感じか。

最新のパッチ(http://alexanderchuranov.com/boost-port/boost-145-to-148-v03.diff)を読むと1.46.1へのアップデートの際に出されたパッチ(http://alexanderchuranov.com/boost-port/boost-1.45-to-1.46.1_attempt05.diff)よりシンプルになってるから、1.49.0への対応もそれほど困難ではないだろう。問題はいつリポジトリに反映されるか、なんだけど、これはもう待つしかないかなあという印象。

そもそもなんでこんなことを調べ始めたかというと、Mac OS X上で書いたboost::randomを使った実装をFreeBSDで実行したらSIGFPEが発生したことが発端。再現サンプルは次の通り:

#include <iostream>
#include <boost/random.hpp>

int main()
{
  boost::mt19937 gen(static_cast<unsigned long>(time(NULL)));
  boost::uniform_smallint<uint32_t> dst(0x00000000u, 0xffffffffu);
  boost::variate_generator<boost::mt19937 &, boost::uniform_smallint<uint32_t> > rand(gen, dst);
  std::cout << rand() << std::endl;
  return 0;
}

上記コードはuint32_tな値を0x00000000〜0xffffffffの範囲で欲しいというもの。これがboost 1.45.0ではSIGFPEが発生し、boost 1.49.0ならば発生しない。

Mac OS XではHomebrewを使ってboost 1.49.0を、FreeBSDではportを使ってboost 1.45.0をインストールしている。boost 1.45.0でSIGFPEが発生する場所は https://github.com/boost-lib/random/blob/1.45.0/include/boost/random/uniform_smallint.hpp#L120 あたり:

    r_base += 1;
    if(r_base % _range == 0) {
      // No quantization effects, good
      _factor = r_base / _range;

経緯の説明は省くが、120行目でゼロ除算が起き例外が発生する。この部分はboost 1.47.0で大きく実装が変更されている。boost公式サイトのリポジトリのログも「Update uniform_smallint」のみで詳細は不明。

そんなこんなでFreeBSD portsで新しいboostをインストールしたいのになぜboost 1.45.0のままなのだろう、と調べ始めた結果がこの記事の前半である。

nested repeat operator + and ? was replaced with '*'

仕事でアセンブリ言語のソース間の依存関係を調べるべく、Rubyでスクリプトを書き、その中で正規表現を使った。その時に出た警告についてメモしておく。
例えば行頭にあるラベルを判別するために:

/^\S+\s+/

と書いた。\S+がラベル、\s+はラベルの直後の空白文字列を表す。その後ラベルはあったりなかったりすることに気付き:

/^(/\S+/)?\s+/

と書いた。さらにラベルは一致はさせるがあとで取り出す必要がないので:

/^(?:/\S+/)?\s+/

と書いた。
ここまで書いてRuby 1.8.7-p72では問題なく動いていたが、先日Ruby 1.9.3-p15へアップグレードしたらnested repeat operator + and ? was replaced with '*'と警告されるようになった。再現すると:

% ruby -e '/(?:\S+)?/'
ruby: warning: nested repeat operator + and ? was replaced with '*': /(?:\S+)?/

こんな感じ。ようは(?:\S+)?(?:\S*)書けるよ、ということを伝えたいのかなと理解した。理解したのはいいが:

/^(?:\S*)\s+/

と書いた場合、ラベルにあたる部分がいかにも「0文字以上の文字列」であるように見えて、やや書いた当初の意図(1文字以上の文字列があったりなかったりする)から外れるような気がする。
しかし上記警告が結構な回数出るのがウザく、なんかもうめんどくさくなってしまい、ヤケで結局のところ:

/^\S*\s+/

と書き直した。ここまで来るとどういう意図で書いたものなのかさえわからなくなりそうだが、警告出されるよりはマシか。