macOSでの動的ロード問題まとめ

主な問題

最近のRuby+Xcodeでは以下の4つの問題があるっぽい

  • dynamic_lookupがXcode 14で警告が出る
  • two-level namespace vs. flat namespace
  • 別の拡張ライブラリのシンボルを参照できない
  • disable-sharedなrubyがfat binaryを読めない

dynamic_lookupがXcode 14で警告が出る

https://bugs.ruby-lang.org/issues/19005 Xcode 14にて以下の警告が出て、拡張ライブラリのコンパイルに失敗する。

ld: warning: -undefined dynamic_lookup may not work with chained fixups

Ventura同梱のRubyは警告を無視している: https://ruby.slack.com/archives/C02A3SL0U/p1667539569008269?thread_ts=1663959379.534979&cid=C02A3SL0U 警告を無視する案: https://github.com/nobu/ruby/tree/cc-wrap

chained fixupsが問題になるのは、シンボル参照時に参照先のシンボルがまだ読み込まれていないとき = アプリ起動時で複数のオブジェクトを一気に読むときだけなので、dynamic_lookupをdlopenによる動的ロードでのみ用いるRubyの場合は問題にならない: https://www.emergetools.com/blog/posts/iOS15LaunchTime https://github.com/ruby/ruby/pull/6193

使わなくする場合には bundle_loader で ruby/librubyを指定すればよい → fat binary問題と別の拡張ライブラリ問題

two-level namespace vs. flat namespace

Executing Mach-O Files

  • two level namespaceだと、他のライブラリと名前が衝突しても死ぬことがなくなる
    • その代わり、-undefined dynamic_lookupをつけないと他のライブラリの関数を呼べなくなる
  • flat namespaceだと他のシンボルと衝突

別の拡張ライブラリのシンボルを参照できない

disable-sharedなrubyがfat binaryを読めない

--enable-sharedついてない環境でビルドした拡張ライブラリが--enable-shared付きの環境で動かないよ〜 fat gemで困るよ〜という話が :cry: https://github.com/rake-compiler/rake-compiler-dock/issues/87 https://ruby.slack.com/archives/C02A3SL0U/p1671524923910499

--disable-shared な ruby 実行ファイルには rb_cObject などのシンボルがあるので -bundle_loader でビルドできるが、それでできた .so を --enable-shared な ruby 実行ファイルには rb_cObject などがない(libruby.so にある)ので require に失敗する、みたいな? https://ruby.slack.com/archives/C02A3SL0U/p1671527703349829

そもそもfat binaryは遅い https://ruby.slack.com/archives/C02A3SL0U/p1671533399524569 https://www.clear-code.com/blog/2019/11/22.html https://github.com/ruby/bigdecimal/pull/148

https://bugs.ruby-lang.org/issues/18912

歴史的には様々なパッケージマネージャがバイナリ配布へと舵を切っていたので、fat binaryはやるしかないと思っているが…git

まとめ

解決案としては代替以下の通りになるんじゃないでしょうか

  • dynamic_lookupがXcode 14で警告が出る → 警告を無視する
  • two-level namespace vs. flat namespace → flat namespace
  • 別の拡張ライブラリのシンボルを参照できない → 用途としてはレアなので、そういうgemにはもうちょっと手間をかけてもらってもいい気がする
  • disable-sharedなrubyがfat binaryを読めない → enable sharedのみにする?

あと、前回の記事も考慮すると、

  • 拡張ライブラリ読み込み用の dlopen に RTLD_GLOBAL でなく RTLD_LOCAL を渡す もやっていい気がする

追記

kateiさん曰く

これ思い出して最新のCLT(for Xcode 14.3)で試したら dynamic_lookup 使っても警告でなくなってました。バイナリ見る限りdynamic_lookupを指定するとchained-fixup自体が無効になるっぽいです。

とのこと。hsbtさんも Xcode 14.3 では Apple Silicon なマシンで Ruby 2.4 以降もビルドできるようになっているぽい と書かれており、どうやら解決したっぽい。Python側で大騒ぎしてくれたおかげなようなので感謝…。

Rubyとnamespaceと拡張ライブラリについて

2020年代モノリスの時代

ここ10年ほど流行っていたマイクロサービスについての理解が深まり、その限界が広く知られるようになってきた。ShopifyのModular Monolithについての記事などは代表例だろう。並行して必要性が叫ばれるようになってきたのがnamespaceだ。RubyKaigi 2023ではshioyamaさんがMultiverse Rubyと題して発表をしていたし、その後tagomorisさんも記事を書いている

Rubyでは以前からnamespaceへの議論が行われてはいたのだが、Ruby 2.0の頃の議論では主にMonkey Patchingによる副作用を局所化するためのものだった。当時の議論とはライブラリ読み込みの局所化という課題が挙がっている点が異なっており、それに伴い技術的な困難も異なる。上述の記事でもそれらの多くは整理されているのだが、この記事ではまだ議論の深まっていない点について考えてみる。

拡張ライブラリ

Rubyにnamespaceを導入するに当たって課題となるのが、拡張ライブラリだ。何が問題なのかを理解するには、まず「拡張ライブラリ」とは一体何なのかを改めて考える必要がある。

拡張ライブラリはつまるところ、DLL(Dynamically Link Library、動的リンクライブラリ、共有ライブラリとも言う)である。Rubyの拡張ライブラリは Init_{ライブラリ名}foo.so ならば Init_foo という名前)という関数を持っており、この関数でクラスやモジュール、メソッドなどを定義する。Rubyrequire "foo.so" などとした場合、Rubydlopen(3) を用いてこのDLLを開き、 Init_foo を呼ぶことでクラスやメソッドなどが実際に定義される。

個々で発生する問題は主に以下の三つとなる * Rubyでそのライブラリを使う際のクラス名・モジュール名 * そのライブラリや依存先ライブラリのシンボル名の衝突 * そのライブラリ内の静的変数

読み込み先のクラス・モジュール名

拡張ライブラリ内でクラスやモジュールを定義する場合、通常は rb_define_module("Foo") などと定義する。この場合、 FooObject の下に定義される。

これは、namespace内では rb_define_module_under(ns, "Foo") などと読み替えるような仕組みを入れればよいだろう。

シンボル解決

拡張ライブラリが他のライブラリのシンボル、具体的には関数や変数を用いている場合、名前が衝突してしまうことがある。あるライブラリの異なるバージョンが持つシンボルは同じことが多いだろう。つまりシンボル名が衝突してしまう。 Linuxにはこのような状況を避けるための dlmopen(3) という関数があるがポータブルではない。 多少の副作用はあるが、おそらく解決策は dlopen への引数に RTLD_GLOBAL ではなく RTLD_LOCAL を指定することと思われる。この場合、他の拡張ライブラリからこの拡張ライブラリのシンボルが見えなくなる。具体例だと libv8.gem のようなgemがこれにあたる。このような治安の悪いgemはそこまで数は多くないとは思うが…。

静的変数

DLL内の静的変数の値が変更できる場合、あるnamespace内での変更が別のnamespaceに波及してしまう。例えば、morisさんの挙げているojの設定の例では、設定情報はライブラリ内の静的変数に保存されている。 これを対策しようと思う場合、同一のgemを複数回メモリ上の別の場所にロードする必要があるように思われる。素直にdlopenを使うとそれはできないので、別の場所にDLLをコピーするなどの対策をとることになるが、倍のメモリを消費することになるので副作用が大きい。 クラス変数なりRuby側で管理している領域に保存するように、gem自体を変更してもらうようにするのが無難かもしれない。

まとめ

拡張ライブラリのnamespace対応における問題は、Ruby側の問題と、DLL側の問題に分類でき、後者の制約は突破することが難しい。その制約の中でもそれなりに一定の機能を持ったものは出来そうなことをまとめました。

『研鑽Rubyプログラミング』をもらった

訳者の角谷さんから『研鑽Rubyプログラミング』をもらったので読みました。

読んだ第一印象は「Effective Perl 第2版: 上級Perlプログラマへと成長できる120の階段みたい」でした。Effective Perlは往年のPerlの名著で、プログラミングにあたって気になるより速いコードの書き方や、よりよいコードの書き方、なぜそれがよいのかが詰め込まれた本で、当時まだプログラミング初心者を脱したばかりのわたしにとって、解説書として、テクニック集として、そして道しるべとして重宝するとともに楽しく繰り返し読んだものです。

『研鑽Rubyプログラミング』の筆者であるJeremyはSequelの作者で、近年はRubyコミッタになって八面六臂の活躍をしている…という話はラムダノートの書籍紹介ページから見ることのできる「本書に寄せて」でもまつもとさんが書いてある通りです。Rubyのユーザーとして長きにわたって経験を積んできたのみならず、Rubyコミッタとして広範囲にわたってRubyソースコードを改善してきた筆者の経験が『研鑽Rubyプログラミング』にはつまっています。

さらに日本語版でお得なのは、RubyKaigiに初期から関わる角谷さんの訳注があることです。例えばシンボルについて解説している部分では、シンボルが本質的には数値であり、実装としては ID という数値型をオブジェクトでラップしたものであることが語られているのですが、角谷さんは訳注として、初期の実装では実際にRubyレベルでも数値型だったこと、 ID をラップしていないシンボルもあることを書いています。いやぁ…、どちらも、特に元々は実装も数値だったことはRubyチョットワカルパーソンとしてはついつい語りたくなっちゃう話なんですよね……。

とまぁ、そんな感じで大変お得です。みなさんそういうの好きですよね?Rubyを書き始めた頃にこの本が手元にあったら、きっと、研鑽の友として手元に置きながら書いていて迷ったときに何度も開いたと思います。やりたいことに対していくつもの書き方がある "There is more than one way to do it" なRuby において、『研鑽Rubyプログラミング』はきっとまだ行くべき道を見定められない人の助けとなることでしょう。

How to accelerate Ruby development; Provide real world use case

The history of decentralization of Ruby, and what you can do for the future

The history of Ruby development is also a history of transferring and separating the power of Ruby's decision from Matz.

Many years ago all Ruby development was done by only Matz. But in the year 1999, Matz1 built a CVS server and allow some people to commit their changes into the repository. They fixes trivial bugs and manages some libraries without Matz's decision.

After about a decade, Rails has come and Ruby spreads all over the world, and people started to complain its release engineering.2 The problem is simply that a release includes both new features and bug fixes. To provide them but separated, Ruby introduced "stable branch" and patch releases (maintenance releases).3 And release engineering including releasing a new version (packaging) is also transferred from Matz. Introducing a bug tracker is also at the similar timing. The teeny versioning was transferred to branch maintainers little later (2.1.x).

In CRuby development, Matz still has a privilege over Core component API design and major/minor versioning. We (and maybe you) know Matz's design decision are generally so good. But can't we also replace his role by some other people or committee?

Some people are trying to steal his design principle. For example akr, a Ruby committer, wrote APIデザインケーススタディ, which describes some case studies of Ruby API designs.

Most people of so-called Ruby core team, I think, had considered whether we can replace Matz's role. But they, including me, seem to conclude cannot. It's hard to explain this feeling. But with the words from Smalltalk context,

Personal Mastery: If a system is to serve the creative spirit, it must be entirely comprehensible to a single individual.

Matz seems to use a black box which provides how complex the idea is, whether the name is suitable for the feature or not, the feature has enough merit to add, and so on. We cannot alternate such scoring black box.

Anyway if you check the log of RubyDeveloperMeeting or Matz's replies to Redmine tickets, you will find there's many replies like "naming" and "use cases". In these days people get well understood about naming issues. But people still don't care about use cases so much. Recently some of Ruby core team write Rails applications as their business (including me) and some are working as full time committer in Rails company like Cookpad and gathers use cases in there. Oneshot mode to coverage introduced in Ruby 2.6 is such feature. But we sometimes don't have enough experiences or imagination to discuss about a suggested feature.

The throughput of oracling the black box is limited. But we don't use its maximum performance yet. If you triage issues and providing/discussing a concrete real world use case for a feature, it will boost the Ruby development.

At this time when we discuss about a feature request, we discuss about how the feature is used in the real world. Then we check whether people who reads the code may misunderstand/confuse or not. You may suggest a magical fun feature but be rejected. It's because of such reason. Naming issues are also because of this reason.

And also, if a ticket has so long comments, it's helpful to summarize previous discussions. It's very helpful. Very helpful.

Conclusion

You can speed up Ruby development with showing a snippet from real world application.

char8_tによせて

C++標準化委員会、ついに文字とは何かを理解する: char8_tという記事が話題だってので、つらつらと書いてみました。

「グリフ」について

グリフ(glyph)という言葉の定義をめぐって でも触れられていますが、「グリフ」という言葉が「字体」を指すのか「字形」を指すのかってのは議論がありますね。文字コードの文脈では普通「字形」の意味だとして話を進めることが多いように思います。

CJK統合漢字について

Wikipediaの記事にまとまっていますが、実際に推進していたのは中国みたいですね。うまくやればあんまり問題なかったんでしょうが、あんまりうまく行かなかったんですが、それでも国ごとにその国の過去にあった文字コードとの互換性は取れているので、実際の所CJK統合漢字ってあんまり問題にはなってないと思うんですよね。中国語フォントと日本語フォントを切り替えないといけないって問題はありますけど、それは仕方なくない?だってさぁ、アルファベットは元から統合されてるんだし。

まぁ、トルコ語のアルファベットは分離しておいた方がよかったと思います。Wikipediaのトルコ語の項目にちらっと示唆されていますが、大文字と小文字の対応が違うんですよね…。

MicrosoftUnicodeといえば、もれなくUTF-16を指す。」について

元々はUnicodeは16bit固定長で、それを使ってWindows NTを実装していたら、いつのまにかUnicodeは16bitには納まらなくなっていて、仕方ないのでUTF-16に拡張したっていう悲しい経緯であって、UTF-16そのものの、あるいはワイド文字のいけてなさとは関係ない気がしました。

ワイド文字について

ワイド文字の歴史

ワイド文字については何度か書いたことがあります。

そもそもワイド文字という概念はUnicode以前からあって、元々はDEC漢字のような日本語UNIX環境の開発から生まれ、日本語UNIX環境がAT&Tの本家UNIXに取り込まれることで世界に紹介され、C89にwchar_tが取り込まれ、C++にも採用されるといった順序になっています。

この本家UNIXの国際化の世界観というのがEUCで、複数の文字集合をG0/G1/G2/G4にマップし、それを内部表現32bit、外部表現は1-4バイトで表現していました。

その他のLinuxとかとかではUTF-32だ、という事が多い。これはコードポイントと一致するエンコードだからだろうか。

これは前述の時代の名残でwchar_tが32bitで定義されていることが多かったため、自然とそこにUTF-32を詰めるようになっただけだと思います。元々そこに詰められていたのはEUCの類だったわけです。

という経緯が分かると、なぜC/C++標準で執拗にUnicode決めうち仕様を避けているのかが分かってくるのではないでしょうか。wchar_tにUnicode以外の何かを詰めたコードは世の中に多数存在するのです。

ワイド文字はなぜいけてないか

さて、なぜワイド文字はいけてないかというと、サロゲートペアは決定的な理由ではないでしょう。16bit Unicode固定長という夢の世界が否定されても、Windows NTやmacOSは既に作られてしまっているわけで、それらとデータをやりとりするにはそれを元にしたUTF-16サロゲートペアでがんばるというのは自然な発想でしょう。またBMP外の文字や絵文字などを処理する際のつらさはUTF-8でもUTF-16でも同じくらい辛いわけで、UTF-16を批判する理由にはなりません。決定的な理由が出てこない限りは、互換性と、UTF-16にあるASCIIとその他の文字を平等に扱うという三方一バイト損的な美しい大義を捨てる理由にはなりません。

では、その決定的な理由とは何か。Web世界とやりとりすることが増えたからでしょう。Webの世界はHTTPの上で成り立っており、HTTPはASCIIを基本として全てが組み上げられています。そんなASCIIの世界をUTF-16で扱うには毎回変換を行う必要がありますし、万が一不正なバイトが入っていた場合のエラーハンドリングを行うにはバイト列のまま扱う処理を用意しなければいけません。そんな事を行うならば最初からバイト列でがんばるか…UTF-8を使った方がよいでしょう。

この結果が90%のWebサイトがUTF-8という現実なのであり、これと同じことがついにC++標準にも及んできたと言うことなのでしょう。

なお、この辺の事情はUnixでのファイルシステムでも同様で、パスがバイト列で表されていて/とヌル文字以外は何でも入るのでだいたいはUTF-8なんだろうけれど例外的な状況も扱えないといけないという事情がありますね。(そもそもUTF-8ファイルシステム用に生まれたんだし)

文字列について

https://togetter.com/li/1301253 で提示されている、「文字列」は文字の列としては扱いづらいという話はその通りだと思うのです。そういう中身が不透明な文字列型は例えば(C++よくわかんないんでCで書きますけど)こんな感じ?

struct {
  rsize_t size;
  int8_t encoding; /* UTF-8, UTF-16, or UTF-32 */
  int8_t valid_p;  /* true, false, or unknown */
  union {
    char8_t *u8;
    char16_t *u16;
    char32_t *u32;
  } chars;
}

前述の通り不正なバイト列もコンテナとしては入れられるけど、文字列用関数に渡すとエラーになるみたいな感じになるのだと思います。でも結局文字列を扱う関数は気合で誰かが書かないといけないし、C++ってそういう関数を各言語という面も少なからずあるので、まずchar8_tから入れたんでしょうかね。

そしてchar8_tについて

と、ここまで考えると一周回って char8_t って本当に必要だったの?という疑問がわいてきます。提案であるchar8_t: A type for UTF-8 characters and strings (Revision 5)を読むに、charにはその環境ごとのdefault localeの文字、つまりWindowsでいえばANSIをいれて、それとは別にUTF-8を入れる型を用意したいって話かな。unsigned charuint8_tでいい気もしますけどね。

ところで、バイト列とUTF-8文字列を区別したいという観点だと、バイト列をunsigned charで扱い、UTF-8文字列をchar8_tで扱うとした場合、文字列は暗黙的にバイト列として扱えるけど、その逆は明示的に指定しないとキャストできないって言う使い心地になるので、それはそれでHTTP絡みのコードを書くときは便利になるかもしれませんね。

観察日記 2018-02-14

配列の全ての要素が等しいか否か

mrkn
配列の全ての要素が等しいことはどう確認したら良いんだろう。
`ary.all? {|e| e == ary[0] }` これかな

usa
ary.uniq.size == 1

mrkn
なるほど > uniq

usa
all?でブロック引数より速そうな予感
いやでもaryがでかくてかつ全然要素が等しくなかったらそうでもないか。

mrkn
`ary.all? {|e| e.foo == ary[0].foo }` の場合はどうでしょう。map.uniq.size がいいかな

usa
all?は全て等しい時に遅いが、序盤で違うとわかったら速い

mrkn
確かに > 序盤で違うとわかったら速い

usa
この辺は予想される集合の傾向で判断するしかないですかねえ。
map.uniq.sizeはmapの結果としての一時配列を作らないようにするには、えーと
Enumerator

mrkn
一瞬 `ary.homogeneous? {|e| e.foo }` って書きたくなったけど、そのまま書いても読めるから大丈夫そうだ。
`ary.lazy.map(&:foo).uniq.size`

naruse
MJITがall?もはやくしてくれるさぁ

mrkn
all? がインライン展開されるのか。

sorah
lazy.uniqいけるのか。
.sizeじゃなくて .count?

usa
lazyだとsizeはまずい

mrkn
lazy 使う場合は count でした

sorah
`[1,1,1,1].lazy.uniq.take(2).count == 1` かなぁ

usa
だいぶテクニカルになってきたな

sorah
>> a = Struct.new(:i) { def n; p i; end }; [a.new(1), a.new(2), a.new(3), a.new(4)].lazy.map(&:n).uniq.take(2).count
1
2
=> 2
>> a = Struct.new(:i) { def n; p i; end }; [a.new(1), a.new(2), a.new(3), a.new(4)].lazy.map(&:n).uniq.count
1
2
3
4
=> 4
こういう差が。 usa なるはや殺しっすよね しかしさすがにここまでくると別名をつけたくなったね。 mrkn take(2) 頭いいな この take(2) は結構感動しました。 usa さすがそらはパイセンですよね。 しかし `homogeneous?` ってわかりやすい名前なのかな。 tadd https://ruby-gnome2.osdn.jp/ja/hiki.cgi?%A5%DC%A5%C3%A5%AF%A5%B9 ruby + homogeneous でぐぐったらGtkが引っかかった mrkn 私が最初に思いついた名前なので、一般的ではないと思います。 ary.same_all? とかかな usa ぼくもsame_allかなあと思いました。 mrkn activesupport にも存在しない knu `[1,1,1,1].each_cons(2).all? { |a,b| a == b }` usa なんとなく遅そうなイメージ knu なぜだろう usa 比較が1回減るのはいいのだけど (e0,e1) (e1,e2) (e2,e3) ... という舐め方が有利なのか不利なのかどっちだろう、という。 knu ブロックが遅い気がする。簡略記法欲しいな usa ブロックはどうしても遅い knu `inject(:+)` はチート 計測すると、条件成立時や末尾に違う値がある場合は uniq が2倍以上速い でも、ショートカットはしてくれないので最初の方に違う値が検出される場合は当然each_consが圧勝 usa 2倍以上かあ。 やはり想定されるデータセットにあわせて選ぶしかないですね。 knu きっとone? があれば lazy.uniq.one? が最適解かな (equality が eql? ってのはおいといて) usa あ、そういうのはまあそういうことで knu ショートカットがないは語弊があった。2つの値だけがずっと続く場合は〜、だ。 one? はたまーにほしいんだよな 何回か前のDeveloperMeetingで出てたか と思ったらある?あれ? usa おっ knu 一つだけが真の場合にtrueを返すのか nilやfalseを考えないなら使えるか `[1,1,1,1].lazy.uniq.one?` でよさそう uniqだから 1 と 1.0 は区別される、one?だから nil や false が入る場合はダメ、という制約付き sorah ブロック無し `.any?` を `.size > 0` 的に使ってるのと同じくらいの生理的嫌悪感がある usa わかる sorah あれ使ってる人は nil, false での動作理解して書いてんのかな、というのが分からなくて混乱するのでやめてほしいんだよな knu お、 `one?(&:object_id)` にしても速度落ちない knu Lispだと=がいくつでも引数取れるか `(apply #'= '(1 1 1 1)) ` ずるい usa ずるいよね knu Rubyのオブジェクト指向はレシーバと引数の間に偉さの違い usa Rubyしぐさ knu zipがたまに気持ち悪い 気持ちは a.zip(b,c) でなく [a,b,c].zip と書きたいんだ… Array.zip(…) が配列を返して Enumerator.zip(…) はEnumerableを返す、というのは悪くないと思うんだけど字面がRubyっぽくないんだよな mrkn 会社のブログで出題してみたら `ary.rotate == ary` というここでは出なかった解が出てきた。 usa おー 遅そうw でも柔軟な発想を感じる。すごい。 mrkn 似たようなので `ary[0..-2] == ary[1..-1]` usa rotateしないからこれはまだ。しかしかっこよさに欠けるな。 いや、これ実はむっちゃ速い? mrkn 配列を作らない usa しかも評価もおそらく短絡してくれる。 mrkn 配列の長さが2以上の時の実装はコレでしょうな。 usa 1の時もいけるのでは ko1 rotate 知らなかった sorah 柔軟な発想だ… usa それを考えた人に今日の :100: をあげよう。 文句なし。

slackのログを上手いことまとめる方法が思いつかなかったので、雑にpreで……。

はじめての投資

投資とは

最近、「投資を始めようかな」と考える人が増えたように思います。この記事を読んでいるあなたもきっとそうでしょう。

アベノミクス、株高、円安、ビットコイン、転職など理由はさまざまでしょうが、リーマンショックを乗り越えた世界の、日本の経済情勢の変化があなたの心にもついに波及したということでしょう。余力のある人にとって、多少のリスクを取って資産を増やそうというのはとてもよい行いです。

そもそも投資とはリソースの最適配分であり、リスクと正面から向き合うことであり、資本主義社会における投票権の行使でもあります。これまであなたは資産の全てを日本円の現預金で保有していたことでしょう。これがどのような意味を持つ行いだったのか改めて考えてみましょう。

2008年のリーマンショックから2012年のアベノミクス開始までは物価や株価は下がり、円高が進んでいましたから、日本円の現預金で資産を持つのは悪くない投資だったと言えるでしょう。この間、あなたの資産は1.5倍くらいになっていたはずです(円建てでは変わっていませんが)

一方で2012年から今までは、消費者物価こそあまり変わっていないものの、株価は上がり円安となりました。この間、あなたの資産は2/3くらいになったともいえます(円建てでは変わっていませんが)

銀行が破綻してかつ預金保護法が守ってくれなかった場合や、極端に物価が上がった場合というような、早々発生しないリスクをおいておいても、上述したような資産価値の変動は日本円で持っていても発生しています。

あなたはこれまで日本円を基準として資産の価値を、リスクを考えてきたと思いますが、それが絶対の基準ではないことが分かりました。株価や物価はもとより、円の価値もドルの価値も日々動いていくとすれば、私達は何をよりどころにして資産価値を把握し、維持・あるいは増やしていく基準としていけばよいのでしょう。健全な精神は健全なKPIの下にこそ宿るというのに、私達は何を信じればいいのでしょう。

国際分散投資

一般論としては、あなたはまず世界経済の長期的な成長を信じる必要があります。3.11があろうと、リーマンショックがあろうと、世界経済はそれを乗り越えて成長していく、世界の資本主義経済の未来に賭ける必要があります。あなたは信じられますか?

これを信じられない方に、私は語る言葉を持ちません。この前提が崩れた世界がどのようなものなのか、年末年始にそれを考えるのは楽しい娯楽かもしれません。とりあえず信じられるものとして先に進みます。

世界経済の成長を信じられるとしても、個々の企業はもちろん、個々の国の経済は信じ切れないわけです。ならばどうするか、色々な企業、色々な国にバラバラに投資する。つまり国際分散投資ということになります。

用語の説明

分散投資をする際に一社一社売買していたら手間がかかって仕方ありません。まずはそういう際の道具を説明しておきます。

  • 証券化: 色々なものを売買しやすくするための手法の一つです
  • 上場: 色々なものを売買しやすくするための手法の一つです
  • 投資信託: 投資家から集めたお金を一定の基準で運用し、手数料を取って利益を配分する金融商品です。株式や債券、不動産など色々なものを扱う投資信託があります。
  • ETF(上場投資信託): 投資信託を上場してより売買しやすくしたものです
  • 債権: 国債社債などです、つまり、国や会社に金を貸して金利を取るというものです
  • REIT(リート): 不動産投資信託 (real estate investment trust)です。その名の通り不動産を扱う投資信託です。
  • コモディティ: 一般には商品、日用品といった意味ですが、この文脈では原油天然ガス、金、プラチナなどをETFにしたものを扱います
  • ノーロード: 売買手数料がかからないという意味です。運用手数料がその分高いわけですが、投資家の利害と運用者の利害が一致しやすいのでよいとされています。(従来型証券会社が売買手数料目当てに無為な売買を客に押し込んでいたという話を聞いたことのある人も多いでしょう)

金融商品での儲け方

金融証券から利益を得る際にはインカムゲインキャピタルゲインという二通りの方法があります。

インカムゲインは端的には利息のことです。具体的には株式なら配当、債権なら受取利息、不動産なら家賃収入です。

キャピタルゲインは値上がり益のことです。株式はもちろん、金融商品はものによって程度の差はあれ価格が変動します。この変動から利益を得ることも出来ます。

金融商品の種類

投資の対象となる金融商品は、種類に応じて以下のような特徴があります

  • 株式: 値動きが大きく(=大きなキャピタルゲインを狙える)、利回りはぼちぼち(=配当収入はぼちぼち)
  • 債権: 値動きが小さく、利回りが高い
  • 不動産: 株式や債券と異なる値動きをし(=リスクを分散出来る)、利回りが比較的高い
  • コモディティ: 物価に連動した動きをする

になります。

地域の分散

次に地域的な分散についてですがおおざっぱには、

  • 国内
  • 先進国: ローリスクローリターン
  • 新興国: ハイリスクハイリターン

に分けられます。

国際分散投資をしよう

さまざまな種類の金融商品と地域分散を理解出来ましたね。

では、投資信託一覧を見て、さまざまな金融商品を組合せ、君だけの最強のポートフォリオを作ってみましょう!

・ ・ ・

できましたか?

ちょっと難しいですよね。そういう方のために最近ではロボアドバイザーというものが作られています。 ロボアドバイザーは何をしてくれるかというと、ユーザーの目指すべきリターン、受け入れられるリスクを加味しながら、適切に分散投資をしてくれます。

適切でない分散投資というのは、一見独立しているかのように見えて実は価格の上下に相関がある複数の商品に投資してしまう場合です。具体例としては一見違う業種、違う国の企業に見えるが、Appleに売り上げを大きく依存している、とかですね。

というわけで証券会社を選んで実際にやってみましょう!どこがいいのって聞かれそうなのでとりあえず二社挙げておくと、

注:二社とも私とは2017年現在と特別な利害関係はありません。

私は松井証券を使っていますが、「世界経済の長期的な成長」以上の何かを信じている場合にはよいですね。例えば当面世界経済は安定するだろうと信じている場合、その分偏った投資(分散出来ていない投資)を行い、的中すればリターンは大きくなります。(外れたら損します)今年はよい年でしたが来年再来年は雲行きがあやしくなるかもしれませんから、先行き分からないときはきちんと分散投資を徹底した方がいいでしょう。とはいえ、自分で考えてギャンブルするのも楽しいので、多少の額で遊んでみるのもよいかもしれません。

明日暴落したらどうするか

株式は元本割れすることがあります。また、それは急速に起きることがあります。 例えば東日本大震災の際にはその後の数日で2割程度下落しています。(その後若干戻りましたが)また、もし急に安倍首相が退陣したら日本の株価は短期間で大きく下落するでしょう。

これについてはすでに答えが出ており、積み立てが正解とされています。これまでに説明した分散がこの三次元世界における分散だとしたら、積み立ては時間方向への分散投資だと言うことができるでしょう。

毎月分配型について

毎月分配型の投資信託というものがあり、それについての議論があります。それを読んで悩む人もいるでしょう。

結論だけ言うと、毎月分配型はリタイア層向けの商品で、この記事の読者向けではありません。毎月分配金を払う事務コストや利益を再投資に回せないのは資産を増やすという観点では悪手です。資産を消費しながら生きていくという観点では悪くないとは思いますが。