2023-02-18 00:29:03 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2023-08-18 15:13:28 +00:00
|
|
|
from contextlib import nullcontext
|
2024-02-09 21:42:33 +00:00
|
|
|
from typing import Literal, Optional, Union
|
2023-02-18 00:29:03 +00:00
|
|
|
|
2022-08-31 04:33:23 +00:00
|
|
|
import torch
|
2022-09-06 00:40:10 +00:00
|
|
|
from torch import autocast
|
2023-08-18 15:13:28 +00:00
|
|
|
|
2023-05-26 00:41:26 +00:00
|
|
|
from invokeai.app.services.config import InvokeAIAppConfig
|
2024-03-11 12:01:48 +00:00
|
|
|
from invokeai.app.services.config.config_default import get_config
|
2022-08-31 04:33:23 +00:00
|
|
|
|
2023-02-18 00:29:03 +00:00
|
|
|
CPU_DEVICE = torch.device("cpu")
|
2023-03-03 05:02:15 +00:00
|
|
|
CUDA_DEVICE = torch.device("cuda")
|
|
|
|
MPS_DEVICE = torch.device("mps")
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-07-27 14:54:01 +00:00
|
|
|
|
2023-02-18 00:29:03 +00:00
|
|
|
def choose_torch_device() -> torch.device:
|
2023-03-03 06:02:00 +00:00
|
|
|
"""Convenience routine for guessing which GPU device to run model on"""
|
2024-03-17 02:25:19 +00:00
|
|
|
config = get_config()
|
2024-03-11 11:57:31 +00:00
|
|
|
if config.device == "auto":
|
2023-08-17 17:47:26 +00:00
|
|
|
if torch.cuda.is_available():
|
|
|
|
return torch.device("cuda")
|
|
|
|
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
|
|
|
|
return torch.device("mps")
|
|
|
|
else:
|
|
|
|
return CPU_DEVICE
|
|
|
|
else:
|
|
|
|
return torch.device(config.device)
|
2022-08-31 04:33:23 +00:00
|
|
|
|
2024-03-27 06:32:08 +00:00
|
|
|
|
2024-03-27 06:28:06 +00:00
|
|
|
def get_torch_device_name() -> str:
|
|
|
|
device = choose_torch_device()
|
|
|
|
return torch.cuda.get_device_name(device) if device.type == "cuda" else device.type.upper()
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2024-03-27 06:32:08 +00:00
|
|
|
|
2024-02-06 02:55:11 +00:00
|
|
|
# We are in transition here from using a single global AppConfig to allowing multiple
|
|
|
|
# configurations. It is strongly recommended to pass the app_config to this function.
|
2024-02-09 21:42:33 +00:00
|
|
|
def choose_precision(
|
|
|
|
device: torch.device, app_config: Optional[InvokeAIAppConfig] = None
|
|
|
|
) -> Literal["float32", "float16", "bfloat16"]:
|
2024-02-06 02:55:11 +00:00
|
|
|
"""Return an appropriate precision for the given torch device."""
|
2024-03-17 02:25:19 +00:00
|
|
|
app_config = app_config or get_config()
|
2023-03-03 06:02:00 +00:00
|
|
|
if device.type == "cuda":
|
2022-09-17 17:56:25 +00:00
|
|
|
device_name = torch.cuda.get_device_name(device)
|
2023-03-03 06:02:00 +00:00
|
|
|
if not ("GeForce GTX 1660" in device_name or "GeForce GTX 1650" in device_name):
|
2024-02-06 02:55:11 +00:00
|
|
|
if app_config.precision == "float32":
|
|
|
|
return "float32"
|
|
|
|
elif app_config.precision == "bfloat16":
|
2024-01-12 18:40:37 +00:00
|
|
|
return "bfloat16"
|
|
|
|
else:
|
|
|
|
return "float16"
|
2023-12-22 00:42:47 +00:00
|
|
|
elif device.type == "mps":
|
2023-07-04 22:05:01 +00:00
|
|
|
return "float16"
|
2023-03-03 06:02:00 +00:00
|
|
|
return "float32"
|
|
|
|
|
2022-09-17 17:56:25 +00:00
|
|
|
|
2024-02-06 02:55:11 +00:00
|
|
|
# We are in transition here from using a single global AppConfig to allowing multiple
|
|
|
|
# configurations. It is strongly recommended to pass the app_config to this function.
|
|
|
|
def torch_dtype(
|
|
|
|
device: Optional[torch.device] = None,
|
|
|
|
app_config: Optional[InvokeAIAppConfig] = None,
|
|
|
|
) -> torch.dtype:
|
2024-02-04 03:55:09 +00:00
|
|
|
device = device or choose_torch_device()
|
2024-02-06 02:55:11 +00:00
|
|
|
precision = choose_precision(device, app_config)
|
2024-01-12 18:40:37 +00:00
|
|
|
if precision == "float16":
|
|
|
|
return torch.float16
|
|
|
|
if precision == "bfloat16":
|
|
|
|
return torch.bfloat16
|
2023-01-17 00:32:06 +00:00
|
|
|
else:
|
2024-01-12 18:40:37 +00:00
|
|
|
# "auto", "autocast", "float32"
|
2023-01-17 00:32:06 +00:00
|
|
|
return torch.float32
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-09-17 17:56:25 +00:00
|
|
|
def choose_autocast(precision):
|
2023-03-03 06:02:00 +00:00
|
|
|
"""Returns an autocast context or nullcontext for the given precision string"""
|
2022-09-17 17:56:25 +00:00
|
|
|
# float16 currently requires autocast to avoid errors like:
|
|
|
|
# 'expected scalar type Half but found Float'
|
2023-03-03 06:02:00 +00:00
|
|
|
if precision == "autocast" or precision == "float16":
|
2022-09-17 17:56:25 +00:00
|
|
|
return autocast
|
|
|
|
return nullcontext
|
2023-02-18 00:29:03 +00:00
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-07-03 14:55:04 +00:00
|
|
|
def normalize_device(device: Union[str, torch.device]) -> torch.device:
|
2023-02-18 00:29:03 +00:00
|
|
|
"""Ensure device has a device index defined, if appropriate."""
|
|
|
|
device = torch.device(device)
|
|
|
|
if device.index is None:
|
|
|
|
# cuda might be the only torch backend that currently uses the device index?
|
|
|
|
# I don't see anything like `current_device` for cpu or mps.
|
2023-03-03 06:02:00 +00:00
|
|
|
if device.type == "cuda":
|
2023-02-18 00:29:03 +00:00
|
|
|
device = torch.device(device.type, torch.cuda.current_device())
|
|
|
|
return device
|