メモの日々


2017年03月03日(金) [長年日記]

[c][c++] exp10()

glibcにexp10という関数があることを知った。pow10もある。

exp10はCやC++の今の標準ライブラリには入っていない。いずれ入るのかな。

手元の環境(CentOS 7, GCC 4.8.5)だと、<cmath>をincludeすればC++でexp10を使えた。Cの場合は_GNU_SOURCEをdefineしてから<math.h>をincludeしないと警告が出る。

[c++] exp10()とstd::pow()とstd::exp()

10のべき乗を計算するプログラムでstd::powを使っていたのだけれど、結構遅い。代わりにexp10を使ったら少し速くなった。exp10を標準化して欲しい。

ところで、10^x は

10^x = (e^log(10))^x = e^(log(10) * x)

と変換できるので、std::expを使っても計算できる。試すと、これが一番速かった。xの範囲によって計算の速度が変わるのかもしれないけれど。以下に、xが0の近辺で10^xを計算するプログラムとその実行結果を示す。

#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>

template<typename F>
void call(const std::string& name, F f) {
  using namespace std::chrono;
  constexpr int n = 10000000;
  constexpr double d = 0.0001;

  const auto s = system_clock::now();
  double r = 1;
  for (int i = -n; i <= n; ++i) {
    r *= f(d * i / n);
  }
  const auto e = system_clock::now();

  std::cout
      << std::setprecision(20)
      << r
      << ", " << duration_cast<milliseconds>(e - s).count() << " ms"
      << ", " << name
      << std::endl;
}

int main() {
  call("std::pow", [](double x) {
    return std::pow(10.0, x);
  });
  call("exp10", [](double x) {
    return exp10(x);
  });
  call("std::exp", [](double x) {
    constexpr double log_10 = std::log(10);
    return std::exp(log_10 * x);
  });
}
1.0389215324922376205, 2650 ms, std::pow
1.0389215324948466446, 2371 ms, exp10
1.0389215324923555261, 1832 ms, std::exp

2017年03月07日(火) [長年日記]

[c][c++] open_memstream()

glibcなどにはopen_memstreamという関数がある。

FILE *open_memstream(char **ptr, size_t *sizeloc);

これは自動的に伸長するバッファをバックエンドに持つFILEポインタを返してくれる。データ長が分からない文字列構築をするときなどに便利。なお、sizelocにNULLを指定することはできない模様。サンプルコードと実行結果をメモ。

#include <stdio.h>
#include <stdlib.h>

int main() {
  char* p;
  size_t size;

  FILE* fp = open_memstream(&p, &size);
  if (!fp) return 1;

  fputs("hello", fp);
  fflush(fp);
  printf("%s %lu\n", p, size);

  fputs("world", fp);
  fflush(fp);
  printf("%s %lu\n", p, size);

  // バッファの先頭から書き込みなおす
  rewind(fp);

  fputs("hi", fp);
  fclose(fp);
  printf("%s %lu\n", p, size);

  free(p); // 要free
  return 0;
}
hello 5
helloworld 10
hi 2

[c++] std::ostringstreamは遅い

ostringstreamが遅いことは以前メモした。これはマルチスレッド動作時のことだったけど、シングルスレッドでもfprintfなどより遅い。

それでもostringstreamにはバッファサイズの管理をしなくてよいという便利なところがある。しかし、stdio.hにも上述のopen_memstreamがあるので、これを使えばostringstreamよりも速い出力文字列ストリームを作れるはず。

以下にopen_memstreamを使ったmy_ostringstreamとstd::ostringstreamの比較プログラムをメモ。my_ostringstreamの方が少しだけ速い。my_ostringstreamは標準ライブラリのストリームに似せているだけでstd::ostreamではないことに注意。

#include <chrono>
#include <iostream>
#include <sstream>
#include <stdexcept>

#include <stdio.h>
#include <stdlib.h>

class my_ostringstream {
public:
  my_ostringstream() : fp_{open_memstream(&buffer_, &buffer_size_)} {
    if (!fp_) throw std::runtime_error{"open_memstream failed"};
  }

  // copyやmoveは面倒
  my_ostringstream(const my_ostringstream&) = delete;
  my_ostringstream& operator=(const my_ostringstream&) = delete;
  my_ostringstream(my_ostringstream&&) = delete;
  my_ostringstream& operator=(my_ostringstream&&) = delete;

  ~my_ostringstream() {
    std::fclose(fp_);
    std::free(buffer_);
  }

  std::string str() {
    std::fflush(fp_);
    return {buffer_};
  }

  explicit operator bool() const { return !is_failed_; }

  my_ostringstream& operator<<(char c) {
    if (std::fputc(c, fp_) == EOF) is_failed_ = true;
    return *this;
  }

  my_ostringstream& operator<<(double d) {
    if (std::fprintf(fp_, "%g", d) < 0) is_failed_ = true;
    return *this;
  }

private:
  char* buffer_;
  std::size_t buffer_size_;
  FILE* fp_;
  bool is_failed_{false};
};

template<typename Out>
void call(const std::string& name, Out&& out) {
  using namespace std::chrono;
  constexpr int n = 1000000;

  const auto s = system_clock::now();
  for (double d = 0; d <= n; d += 0.1) {
    out << d << ' ';
  }
  const auto e = system_clock::now();

  std::cout
      << out.str().size()
      << ", " << duration_cast<milliseconds>(e - s).count() << " ms"
      << ", " << name
      << std::endl;
}

int main() {
  call("my_ostringstream", my_ostringstream{});
  call("std::ostringstream", std::ostringstream{});
}
70688902, 6103 ms, my_ostringstream
70688902, 7639 ms, std::ostringstream

2017年03月15日(水) [長年日記]

[xrea][tdiary] またXREAのシステムが更新され日記が表示されなくなっていた

3/12にこのサーバがリニューアルされたようで、その後からこの日記がエラーで表示できなくなっていた模様。前回は2年前か。

FAQのQ8を参考に、tDiaryのCGIスクリプトのRubyのパスを /usr/local/bin/ruby2.2 に変更したら動くようになった。tDiary 4.1.3(古いバージョンです)にバンドルされているgemがRuby 2.2までに対してしか用意されていないからかな。

サーバが更新されたのはありがたい。


2017年03月23日(木) [長年日記]

[hard] HPのプリンタ ENVY 4520 を買った

キヤノンのプリンタが使えなくなったので、新しいプリンタを買った。

買ったのはHPのENVY4520という複合機タイプのプリンタ。

Amazonが安かったのだけれど在庫が無かったのでヨドバシ.comで初めて買ってみた。9000円くらい。

HP製にしたのは、「カラーインクを外して黒インクだけで印刷」できることを公式に謳っているから。キヤノン製のプリンタなどでもカラーインクなしでの印刷はできるみたいなんだけれど、公式ウェブサイト上に説明がなく隠し機能みたいな扱いなので安心できなかった。

半月くらい使って、不満はない。初複合機なのだけれどコピーができるのは便利。インクの取り付け方法の説明が分かりにくくて手間取ったという問題はあった。


2017年03月28日(火) [長年日記]

[tdiary][xrea] 日記の検索ができなくなっていた

この日記の検索ができていないことに気づいた。サーバの更新が原因なのだろう。

検索にはpnamazuを使っていたのだけれど、

  • 検索結果が表示されない。
  • 検索インデックスを作れない。

という状態になっていた。後者はpnamazuは無関係で、/usr/local/bin/mknmz が存在しなくなったからみたい。前者の原因はわからない。

仕方ないので、tDiaryに付属している search-default プラグインを使うようにしてみた。使い方の説明が見当たらないが、tDiary-develのアーカイブから「<%= search_input_form('') %>」と書けばいいと知った。

これで一応検索ができるようにはなった。不格好だけれど。


2017年03月29日(水) [長年日記]

[howto][dev] submoduleを含むGitリポジトリのclone

Gitのサブモジュールの扱い方を覚えられないのでメモ。Gitのバージョンは 1.8.3.1。

git clone後にサブモジュールの内容を取得する

サブモジュールを含んでいるGitリポジトリを手元にコピーすることを考える。普通にgit cloneを実行するだけだとサブモジュールの中身は空。サブモジュールの中身をダウンロードするには

% git submodule update --init --recursive

を実行するのがよさそう。--initは「git submodule init」を兼ねるためのオプション、--recursiveはサブモジュールがネスとしているケースにも対応するためのオプション。

git clone時にサブモジュールの内容を取得する

git cloneと同時にサブモジュールの内容を取得することもできて、そのためにはgit cloneに--recurse-submodulesオプションを追加する。

% git clone --recurse-submodules <repogitory>

最新のサブモジュールの内容を取得する

上記で取得できるサブモジュールの内容は、clone元のリポジトリに記録されているバージョンになる。サブモジュールの最新版を取得する方法は以前メモしているのだけれど、foreachとか使わなくても

% git submodule update --remote

でできそう。今知ったことなのでよくわかってないけど。

参考