feat(cli): --threads для управления CPU-потоками + бенчмарк CUDA compute_type
- Зачем:
- CTranslate2 по умолчанию использует 4 потока; на многоядерных CPU (8+ ядер)
это неоптимально — --threads 8 даёт +13% ускорения.
- Не было данных по int8_float32/int8_float16 на NVIDIA GPU.
- Что:
- --threads / -t: новый CLI-флаг, пробрасывается через load_model →
backend.create_model(cpu_threads=...) → WhisperModel(cpu_threads=...).
- Валидация min=0 на входе (typer), Backend протокол синхронизирован.
- docs/gpu.md: результаты бенчмарка 6 комбинаций CUDA compute_type
(medium/large-v3 × float16/int8_float32/int8_float16) на двух файлах
(16 мин и 46 мин). Ключевой вывод: float16 — оптимальный дефолт;
large-v3 ненадёжен на длинных записях.
- README: --threads добавлен в таблицу опций.
- Фикс теста: test_resolve_repo_explicit_unsupported_pair_raises обновлён
под добавление medium fp16 модели.
- Проверка:
- uv run pytest: 157 passed.
- transcribe file.mp4 --device cpu --threads 8: 277с vs 320с (дефолт).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8ae74d2748
commit
857267763c
10 files changed
+226
-40
No files matched your search
@@ -862,3 +862,62 @@ def test_cli_openvino_alias_resolves_to_gpu(tmp_path):
|
||||
assert out.exit_code == 0
|
||||
# detect_device("openvino") resolved to "openvino-gpu", load_model receives it
|
||||
assert mock_load_model.call_args[0][1] == "openvino-gpu"
|
||||
|
||||
|
||||
# === --threads ===
|
||||
|
||||
|
||||
def test_cli_threads_passed_to_load_model(tmp_path):
|
||||
"""--threads передаётся в load_model как cpu_threads."""
|
||||
audio = tmp_path / "test.mp3"
|
||||
audio.write_bytes(b"fake")
|
||||
result = _make_result()
|
||||
model = _make_model()
|
||||
backend = _make_backend()
|
||||
tfr = _make_tfr(result=result, model=model, backend=backend)
|
||||
mock_load_model = MagicMock(return_value=(model, "cpu", backend, "/models/medium"))
|
||||
|
||||
with (
|
||||
patch("local_transcriber.cli.load_config", return_value={}),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.load_model", mock_load_model),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
):
|
||||
out = runner.invoke(app, [str(audio), "--threads", "8"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert mock_load_model.call_args.kwargs["cpu_threads"] == 8
|
||||
|
||||
|
||||
def test_cli_threads_default_zero(tmp_path):
|
||||
"""Без --threads load_model получает cpu_threads=0."""
|
||||
audio = tmp_path / "test.mp3"
|
||||
audio.write_bytes(b"fake")
|
||||
result = _make_result()
|
||||
model = _make_model()
|
||||
backend = _make_backend()
|
||||
tfr = _make_tfr(result=result, model=model, backend=backend)
|
||||
mock_load_model = MagicMock(return_value=(model, "cpu", backend, "/models/medium"))
|
||||
|
||||
with (
|
||||
patch("local_transcriber.cli.load_config", return_value={}),
|
||||
patch("local_transcriber.cli.validate_input_file", return_value=audio),
|
||||
patch("local_transcriber.cli.detect_device", return_value="cpu"),
|
||||
patch("local_transcriber.cli.load_model", mock_load_model),
|
||||
patch("local_transcriber.cli._transcribe_file", return_value=tfr),
|
||||
patch("local_transcriber.cli.write_transcript"),
|
||||
):
|
||||
out = runner.invoke(app, [str(audio)])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert mock_load_model.call_args.kwargs["cpu_threads"] == 0
|
||||
|
||||
|
||||
def test_cli_threads_negative_rejected(tmp_path):
|
||||
"""--threads с отрицательным значением отклоняется typer (min=0)."""
|
||||
audio = tmp_path / "test.mp3"
|
||||
audio.write_bytes(b"fake")
|
||||
out = runner.invoke(app, [str(audio), "--threads", "-1"])
|
||||
assert out.exit_code != 0
|
||||
Reference in new issue
Block a user