Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: --runtime_dir 指定されるランタイムの要件を明確化 #1173

Closed
1 of 3 tasks
tarepan opened this issue Apr 6, 2024 · 11 comments
Closed
1 of 3 tasks
Assignees

Comments

@tarepan
Copy link
Contributor

tarepan commented Apr 6, 2024

追記: #1173 (comment) にてバグでなく仕様と明確化。案内を明確化する方針

不具合の内容

概要: 現行の CORE に対して --runtime_dir が効かない

現象・ログ

CORE 0.15.2 をダウンロードし、音声ライブラリ (libvoicevox_core.so) とランタイム (libonnxruntime.so.1.13.1) を別のディレクトリに入れ、音声ライブラリを --voicelib_dir・ランタイムを --runtime_dir で指定すると以下のようにクラッシュする:

$ python run.py --voicelib_dir="${CORENAME}" --runtime_dir="${CORENAME}_mirror"
Warning: cpu_num_threads is set to 0. Setting it to half of the logical cores.
Traceback (most recent call last):
  File "/workspaces/voicevox_engine/voicevox_engine/core/core_wrapper.py", line 360, in load_core
    return CDLL(str((core_dir / core_name).resolve(strict=True)))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/ctypes/__init__.py", line 376, in __init__
    self._handle = _dlopen(self._name, mode)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: libonnxruntime.so.1.13.1: cannot open shared object file: No such file or directory

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/workspaces/voicevox_engine/run.py", line 1630, in <module>
    main()
  File "/workspaces/voicevox_engine/run.py", line 1542, in main
    cores = initialize_cores(
            ^^^^^^^^^^^^^^^^^
  File "/workspaces/voicevox_engine/voicevox_engine/core/core_initializer.py", line 115, in initialize_cores
    load_core_library(core_dir)
  File "/workspaces/voicevox_engine/voicevox_engine/core/core_initializer.py", line 96, in load_core_library
    core = CoreWrapper(use_gpu, core_dir, cpu_num_threads, load_all_models)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/voicevox_engine/voicevox_engine/core/core_wrapper.py", line 562, in __init__
    self.core = load_core(core_dir, use_gpu)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/voicevox_engine/voicevox_engine/core/core_wrapper.py", line 362, in load_core
    raise RuntimeError(f"コアの読み込みに失敗しました:{err}")
RuntimeError: コアの読み込みに失敗しました:libonnxruntime.so.1.13.1: cannot open shared object file: No such file or directory

再現手順

GitHub Codespaces, Linux Py3.11 CPU

別ディレクトリに配置するとクラッシュする:

python -m pip install -r requirements.txt -r requirements-dev.txt -r requirements-test.txt
VERSION="0.15.2"; OS="linux"; ARCHITECTURE="x64"; PROCESSOR="cpu";
CORENAME="voicevox_core-${OS}-${ARCHITECTURE}-${PROCESSOR}-${VERSION}"
curl -L "https://github.com/VOICEVOX/voicevox_core/releases/download/${VERSION}/${CORENAME}.zip" -o "${CORENAME}.zip"
unzip "${CORENAME}.zip"
ls "${CORENAME}"
# README.txt  VERSION  libonnxruntime.so.1.13.1  libvoicevox_core.so  model  voicevox_core.h

# ---------------------------------------------------------------------------
# Test1: ランタイムと音声ライブラリが別ディレクトリにあり、それぞれを指定する
mkdir "${CORENAME}_mirror"
mv "${CORENAME}/libonnxruntime.so.1.13.1" "${CORENAME}_mirror"
ls "${CORENAME}"
# README.txt  VERSION  libvoicevox_core.so  model  voicevox_core.h
ls "${CORENAME}_mirror"
# libonnxruntime.so.1.13.1
python run.py --voicelib_dir="${CORENAME}" --runtime_dir="${CORENAME}_mirror"
# (中略)
# RuntimeError: コアの読み込みに失敗しました:libonnxruntime.so.1.13.1: cannot open shared object file: No such file or directory

ランタイムを移動しないと正常に起動する:

# ---------------------------------------------------------------------------
# Test2: ランタイムと音声ライブラリが同ディレクトリにあり、それぞれを指定する
mv "${CORENAME}_mirror/libonnxruntime.so.1.13.1" "${CORENAME}"
ls "${CORENAME}"
# README.txt  VERSION  libonnxruntime.so.1.13.1  libvoicevox_core.so  model  voicevox_core.h
python run.py --voicelib_dir="${CORENAME}" --runtime_dir="${CORENAME}"
# (中略)
# INFO:     Application startup complete.
# INFO:     Uvicorn running on http://localhost:50021 (Press CTRL+C to quit)

ランタイムを移動しない場合、--runtime_dir 無しでも起動する(異常挙動):

# ---------------------------------------------------------------------------
# Test3: ランタイムと音声ライブラリが同ディレクトリにあり、`voicelib_dir` のみ指定する
# !!!!!! 動的ライブラリが on-memory かもしれないので実行環境を作り直し !!!!!!
python -m pip install -r requirements.txt -r requirements-dev.txt -r requirements-test.txt
VERSION="0.15.2"; OS="linux"; ARCHITECTURE="x64"; PROCESSOR="cpu";
CORENAME="voicevox_core-${OS}-${ARCHITECTURE}-${PROCESSOR}-${VERSION}"
python run.py --voicelib_dir="${CORENAME}"
# (中略)
# INFO:     Application startup complete.
# INFO:     Uvicorn running on http://localhost:50021 (Press CTRL+C to quit)

期待動作

正常にランタイムがロードされ ENGINE が起動する。

VOICEVOXのバージョン

ENGINE master 70d0d8d
CORE 0.15.2

OSの種類/ディストリ/バージョン

  • Windows
  • macOS
  • Linux
@tarepan
Copy link
Contributor Author

tarepan commented Apr 6, 2024

原因調査

load_runtime_lib() へデバック用 print を以下のように差し込み、上記のクラッシュ条件で実行した。

load_runtime_lib()

def load_runtime_lib(runtime_dirs: list[Path]) -> None:
    """
    コアの実行に必要な依存 DLL をロードする。検索対象ディレクトリは引数 `runtime_dirs` およびシステム検索対象ディレクトリ。

    Args:
        runtime_dirs - 直下に DLL が存在するディレクトリの一覧
    """
    # `lib_file_names`は「ENGINE が利用可能な DLL のファイル名一覧」である
    # `lib_names` は「ENGINE が利用可能な DLL のライブラリ名一覧」である(ライブラリ名は `libtorch.so.1.0` の `torch` 部分)
    if platform.system() == "Windows":
        # DirectML.dllはonnxruntimeと互換性のないWindows標準搭載のものを優先して読み込むことがあるため、明示的に読み込む
        # 参考 1. https://github.com/microsoft/onnxruntime/issues/3360
        # 参考 2. https://tadaoyamaoka.hatenablog.com/entry/2020/06/07/113616
        lib_file_names = [
            "torch_cpu.dll",
            "torch_cuda.dll",
            "DirectML.dll",
            "onnxruntime.dll",
        ]
        lib_names = ["torch_cpu", "torch_cuda", "onnxruntime"]
    elif platform.system() == "Linux":
        lib_file_names = ["libtorch.so", "libonnxruntime.so"]
        lib_names = ["torch", "onnxruntime"]
    elif platform.system() == "Darwin":
        lib_file_names = ["libonnxruntime.dylib"]
        lib_names = ["onnxruntime"]
    else:
        raise RuntimeError("不明なOSです")

    # 引数指定ディレクトリ直下の DLL をロードする
    for runtime_dir in runtime_dirs:
        for lib_file_name in lib_file_names:
            try:
                print(f"[a] path resolving...")
                libpath = str((runtime_dir / lib_file_name).resolve(strict=True))
                print(f"[a] path resolved: {libpath}")
                CDLL(libpath)
                print(f"[a] Runtime load successed!")
            except OSError as err:
                print(f"[a] Runtime load failed: {err}")
                pass

    # システム検索ディレクトリ直下の DLL をロードする
    for lib_name in lib_names:
        try:
            print(f"[b] path resolving...")
            libpath = find_library(lib_name)
            print(f"[b] path resolved: {libpath}")
            CDLL(libpath)
            print(f"[b] Runtime load successed!")
        except (OSError, TypeError) as err:
            print(f"[b] Runtime load failed: {err}")
            pass

実行結果:

@tarepan ➜ /workspaces/voicevox_engine (latest_codespaces) $ python run.py --voicelib_dir="${CORENAME}" --runtime_dir="${CORENAME}_mirror"
Warning: cpu_num_threads is set to 0. Setting it to half of the logical cores.
[a] path resolving...
[a] Runtime load failed: [Errno 2] No such file or directory: 'voicevox_core-linux-x64-cpu-0.15.2_mirror/libtorch.so'
[a] path resolving...
[a] Runtime load failed: [Errno 2] No such file or directory: 'voicevox_core-linux-x64-cpu-0.15.2_mirror/libonnxruntime.so'
[b] path resolving...
[b] path resolved: None
[b] Runtime load successed!
[b] path resolving...
[b] path resolved: None
[b] Runtime load successed!
Traceback (most recent call last):
(中略)
RuntimeError: コアの読み込みに失敗しました:libonnxruntime.so.1.13.1: cannot open shared object file: No such file or directory

[a] ではパス解決がコケて CDLL() までそもそもたどり着いていない。
[b] では一見すると load できているが path が None なのでこちらもパス解決がコケている。
つまりload_runtime_lib() はコケているだけで何もロードできていない。ゆえに ENGINE がクラッシュする。

@tarepan tarepan added 要議論 実行する前に議論が必要そうなもの 状態:必要性議論 必要性を議論している状態 and removed OS:linux labels Apr 6, 2024
@qryxip
Copy link
Member

qryxip commented Apr 9, 2024

あまり調べずに発言しますが、#300 (comment)も同じ理由(バージョン無しの.dylibしか対象にならないため、libonnxruntime.1.9.0.dylibが対象にならない)だったりしないかなと思いました。


ランタイムを移動しない場合、--runtime_dir 無しでも起動する(異常挙動):

これについては、LinuxとmacOSではrpathが効いているためだと思います。どちらかというと意図されている挙動なんじゃないかと。
VOICEVOX/voicevox_core#203
VOICEVOX/voicevox_core#251
https://github.com/VOICEVOX/voicevox_core/blob/0.15.3/crates/voicevox_core_c_api/build.rs

@tarepan
Copy link
Contributor Author

tarepan commented Apr 9, 2024

#300 (comment) も同じ理由 ... だったりしないかなと

👍️
load_runtime_lib() は 2 年前から基本構造が変わっていないので、当初からバージョン番号周りのバグを抱え続けていた可能性があります。


LinuxとmacOSではrpathが効いているため ... どちらかというと意図されている挙動

情報ありがとうございます!
「ENGINE CDLL() によるランタイム on-memory 化とは別に、CORE ビルド設定時に与えられる動的ライブラリ指定でランタイムアクセス(CORE内パス解決+ロード)がなされている」という認識で合っているでしょうか?

追記: ココ の話と一緒?


@Hiroshiba
「あると思っていた機能(ランタイム指定)が実はずっと壊れていた」といった状況のようです。
バグ修正するとある意味で新規機能追加になる(なってしまう)状態です。
いきなりバグ修正してもよいでしょうか?
何か事前検証すべきことありそうでしょうか?

@Hiroshiba
Copy link
Member

Hiroshiba commented Apr 9, 2024

バグじゃないという認識です!
現状はonnxruntime.dllを読み込む仕様かなと。

それっぽいファイルがあれば読み込むようにするのは、ちょっとだけ検討しても良い気がしました。
たとえばonnxruntime-hoge.dllのようなのがあった場合読み込むのかとか、onnxruntime.1.dllみたいなバックアップっぽいものがあるときどうするかとか、2つ候補があったときどうするのかとか。
・・・どういう挙動が良いんですかね・・・。

個人的には今のままでもまあ良いのではという気もします。
追記:ドキュメントはもう少し丁寧にしてあげても良いかも

@tarepan
Copy link
Contributor Author

tarepan commented Apr 9, 2024

バグじゃないという認識です!
現状はonnxruntime.dllを読み込む仕様

「製品版 CORE(voicevox_core-linux-x64-cpu-0.15.3.zip)に含まれる libonnxruntime.so.1.13.1 はサポート外であり、各自で ONNXRuntime を独自ダウンロードし取り出した libonnxruntime.so ファイルのみがサポート対象である」という認識で合っているでしょうか?

@Hiroshiba
Copy link
Member

Hiroshiba commented Apr 9, 2024

どれがサポート対象かという意図はなく、libonnxruntime.soを読み込む仕様という認識です!
libonnxruntime.so.1.13.1もリネームすれば使えると思います。

(わりと普通な仕様だと思っているのですが、認識おかしかったらご指摘ください 🙇 )

@tarepan
Copy link
Contributor Author

tarepan commented Apr 9, 2024

リネームすれば使える

👍️
なるほどです。リネームやシンボリックリンクで linker name のファイル (libonnxruntime.so) を作ってそれを使う感じですね。

わりと普通な仕様だと思っているのですが、認識おかしかったら

私もサッパリこの辺わからないので簡単に調査しました。
「共有ライブラリは導入時にシンボリックリンクを張る」が標準的な作法であるようです(参考: ldconfig コマンド、Program Library HOWTO)。
なので「libonnxruntime.soを読み込む仕様」がプログラマー的には普通な仕様と考えます。


リネームを追加し、上記のクラッシュクラッシュ条件で実行した。

$ python -m pip install -r requirements.txt -r requirements-dev.txt -r requirements-test.txt
$ VERSION="0.15.2"; OS="linux"; ARCHITECTURE="x64"; PROCESSOR="cpu";
$ CORENAME="voicevox_core-${OS}-${ARCHITECTURE}-${PROCESSOR}-${VERSION}"
$ curl -L "https://github.com/VOICEVOX/voicevox_core/releases/download/${VERSION}/${CORENAME}.zip" -o "${CORENAME}.zip"
$ unzip "${CORENAME}.zip"
$ ls "${CORENAME}"
README.txt  VERSION  libonnxruntime.so.1.13.1  libvoicevox_core.so  model  voicevox_core.h
$ mkdir "${CORENAME}_mirror"
$ mv "${CORENAME}/libonnxruntime.so.1.13.1" "${CORENAME}_mirror"
$ ls "${CORENAME}"
README.txt  VERSION  libvoicevox_core.so  model  voicevox_core.h
$ mv "${CORENAME}_mirror/libonnxruntime.so.1.13.1" "${CORENAME}_mirror/libonnxruntime.so"
$ ls "${CORENAME}_mirror"
libonnxruntime.so
$ python run.py --voicelib_dir="${CORENAME}" --runtime_dir="${CORENAME}_mirror"
(中略)
INFO:     Application startup complete.
INFO:     Uvicorn running on http://localhost:50021 (Press CTRL+C to quit)

クラッシュせず、正常読み込みに成功した。


結論として「現行の CORE に対して --runtime_dir が効かない」に対する回答は「ランタイムの linker name ファイルを読み込む仕様なので、CORE 内に real name で保存されているランタイムファイルを linker name へリネームしてから実行すればよい」となります。

前提知識不足が原因でした、@Hiroshiba さん @qryxip さんご協力ありがとうございました。


追記:ドキュメントはもう少し丁寧にしてあげても良いかも

ENGINE 利用者の想定レベルで書くことが変わりそうな印象を受けました。
手をいれるべき README.md はこの辺かと思います。

音声ライブラリを直接指定する
VOICEVOX Core の zip ファイルを解凍したディレクトリを--voicelib_dir引数で指定します。
また、コアのバージョンに合わせて、libtorchやonnxruntimeのディレクトリを--runtime_dir引数で指定します。

共有ライブラリの作法がわかるユーザー向けには「libtorchやonnxruntime(共有ライブラリ)」との注釈が有効そうです。
共有ライブラリの作法がわからないユーザー向けは追加しますか?

@tarepan tarepan added 状態:実装 実装をおこなっている状態 機能向上 and removed 状態:必要性議論 必要性を議論している状態 バグ labels Apr 9, 2024
@tarepan tarepan self-assigned this Apr 9, 2024
@tarepan tarepan added 状態:設計 設計をおこなっている状態 and removed 状態:実装 実装をおこなっている状態 labels Apr 9, 2024
@tarepan tarepan changed the title bug: --runtime_dir 指定が機能せず ENGINE がクラッシュする add: --runtime_dir 指定されるランタイムの要件を明確化 Apr 9, 2024
@Hiroshiba
Copy link
Member

Hiroshiba commented Apr 9, 2024

どう案内すべきか難しいですね…。

「共有ライブラリ」は、説明の何処かで「動的ライブラリ」と読んでいる場合はそちらに合わせた方が良いかもです!

わからない人向けには「runtime_dir」引数の詳しい説明を書く場所があればそこがよさそうですが、無いのでいったん無しでも良いかなぁと思いました。
OSごとにファイル名が違ってたりしてどうしても説明にスペースがいるんですよね…。
将来そういう場所ができたら(より詳しい粒度で起動時引数を説明するドキュメントが増えたら)説明を書くのが良いのかなと

@qryxip
Copy link
Member

qryxip commented Apr 9, 2024

わりと普通な仕様だと思っているのですが、認識おかしかったら

なので「libonnxruntime.soを読み込む仕様」がプログラマー的には普通な仕様と考えます。

$LD_LIBRARY_PATHとかにlibonnxruntime.soを入れてもロード時にはlibonnxruntime.so.*.*.*の代わりにはならないので、感覚に反する人もいるかもしれません。libvoicevox_coreにlddをかけたらlibonnxruntime.so.*.*.*の方が出てきますし。
(逆にdlopenでlibonnxruntimeを直接ロードする場合、dlopenSONAMEしか見てないらしいので真名(SONAME)が合っていればlibonnxruntime.soでもaでもOK)

@qryxip
Copy link
Member

qryxip commented Apr 10, 2024

↑ Linuxを使ってると「システムの共有ライブラリをアップデートしたら、システムのパッケージマネージャの管理外のバイナリ(例: Cargoで入れたやつ)がリンクエラーで動かなくなった」ということが頻繁に発生したりします…

@Hiroshiba
Copy link
Member

まあたしかに。
かといって合ってないバージョンのlibonnxruntime.so.*.*.*を読み込もうとするのも感覚に反しそうですね。
あとwindowsでは仕様が違うとかも有り得そうですね・・・。

とりあえずバージョン情報のないもののみを読み込む仕様で良いのではと思いました!
依存してる正しいバージョンを取ってくる形であればバージョンありのも取得して良いと思います。が、そこまでこだわらなくても良いのかなと。

@tarepan tarepan closed this as completed Apr 30, 2024
@tarepan tarepan removed 要議論 実行する前に議論が必要そうなもの 状態:設計 設計をおこなっている状態 labels Apr 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants