2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MeCabで一度に解析できる限界

Last updated at Posted at 2020-07-14

たまたま見かけた記事でmecabのバグと書いてたので調べたメモ。

結論:2,621,440文字が限界

※1文字を2byteで単純計算した場合

これはcommon.hで定義されてる値、MAX_INPUT_BUFFER_SIZEを見ればわかる.

# define NBEST_MAX 512
# define NODE_FREELIST_SIZE 512
# define PATH_FREELIST_SIZE 2048
# define MIN_INPUT_BUFFER_SIZE 8192
# define MAX_INPUT_BUFFER_SIZE (8192*640)
# define BUF_SIZE 8192

8192×640byteのデータを最大で受け取りそうなことが分かる.つまり8192×640÷2で2,621,440文字
以上.

※ソースはMeCab開発者の工藤拓さんがgithubに公開しているものを引用しています(参考)

おまけ

※これ以降はソースを見た所見のような何かです.
実際にMAX_INPUT_BUFFER_SIZEを使用している処理はtagger.cppのみだった(tagger.cpp:1213-1276行目抜粋).

size_t ibufsize = std::min(MAX_INPUT_BUFFER_SIZE,
                             std::max(param.get<int>
                                            ("input-buffer-size"),
                                            MIN_INPUT_BUFFER_SIZE));

  const bool partial = param.get<bool>("partial");
  if (partial) {
    ibufsize *= 8;
  }

  MeCab::scoped_array<char> ibuf_data(new char[ibufsize]);
  char *ibuf = ibuf_data.get();

  MeCab::scoped_ptr<MeCab::Tagger> tagger(model->createTagger());

  if (!tagger.get()) {
    WHAT_ERROR("cannot create tagger");
  }

  for (size_t i = 0; i < rest.size(); ++i) {
    MeCab::istream_wrapper ifs(rest[i].c_str());
    if (!*ifs) {
      WHAT_ERROR("no such file or directory: " << rest[i]);
    }

    while (true) {
      if (!partial) {
        ifs->getline(ibuf, ibufsize);
      } else {
        std::string sentence;
        MeCab::scoped_fixed_array<char, BUF_SIZE> line;
        for (;;) {
          if (!ifs->getline(line.get(), line.size())) {
            ifs->clear(std::ios::eofbit|std::ios::badbit);
            break;
          }
          sentence += line.get();
          sentence += '\n';
          if (std::strcmp(line.get(), "EOS") == 0 || line[0] == '\0') {
            break;
          }
        }
        std::strncpy(ibuf, sentence.c_str(), ibufsize);
      }
      if (ifs->eof() && !ibuf[0]) {
        return false;
      }
      if (ifs->fail()) {
        std::cerr << "input-buffer overflow. "
                  << "The line is split. use -b #SIZE option." << std::endl;
        ifs->clear();
      }
      const char *r = (nbest >= 2) ? tagger->parseNBest(nbest, ibuf) :
          tagger->parse(ibuf);
      if (!r)  {
        WHAT_ERROR(tagger->what());
      }
      *ofs << r << std::flush;
    }
  }

  return EXIT_SUCCESS;

# undef WHAT_ERROR

このコードからMeCab.Tagger.parse()の処理では最大でもMAX_INPUT_BUFFER_SIZEを超えないことが分かる.
次にstring_buffer.htagger.cppから分かるラティスの解析について.(string_buffer.h:15-37行目抜粋)

bool StringBuffer::reserve(size_t length) {
  if (!is_delete_) {
    error_ = (size_ + length >= alloc_size_);
    return (!error_);
  }

  if (size_ + length >= alloc_size_) {
    if (alloc_size_ == 0) {
      alloc_size_ = DEFAULT_ALLOC_SIZE;
      ptr_ = new char[alloc_size_];
    }
    size_t len = size_ + length;
    do {
      alloc_size_ *= 2;
    } while (len >= alloc_size_);
    char *new_ptr = new char[alloc_size_];
    std::memcpy(new_ptr, ptr_, size_);
    delete [] ptr_;
    ptr_ = new_ptr;
  }

  return true;
}

領域の取得を行っているこのreserveはtagger.cppでのみ呼ばれている.(tagger.cpp:733-741行目抜粋)

LatticeImpl::LatticeImpl(const Writer *writer)
    : sentence_(0), size_(0), theta_(kDefaultTheta), Z_(0.0),
      request_type_(MECAB_ONE_BEST),
      writer_(writer),
      ostrs_(0),
      allocator_(new Allocator<Node, Path>) {
  begin_nodes_.reserve(MIN_INPUT_BUFFER_SIZE);
  end_nodes_.reserve(MIN_INPUT_BUFFER_SIZE);
}

そしてLatticeImplはラティスを実体化したときに実行されている(と思う).(tagger.cpp:227-239抜粋)

class LatticeImpl : public Lattice {
 public:
  explicit LatticeImpl(const Writer *writer = 0);
  ~LatticeImpl();

  // clear internal lattice
  void clear();

  bool is_available() const {
    return (sentence_ &&
            !begin_nodes_.empty() &&
            !end_nodes_.empty());
  }

これらのことから、ラティスの解析ではメモリが足りなくなると領域を2倍してmemcpyで取得してるっぽいのでそっちは限界無さそうなことが分かる(勿論メモリを食い潰したら終わるはずだがその前にMAX_INPUT_BUFFER_SIZEで止まると思われる).

その他の参考

mecab.h:lattice等、構造体の定義 etc
libmecab.cpptagger.cppのmutable_lattice()とかを追跡してたらたどり着いたmecab_model_new_lattice()等、modelに紐づいた関数の定義 etc

END.

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?