From 1eb884a249078b7ee1acbd3bce09044aa62dc66c Mon Sep 17 00:00:00 2001 From: Qiyuan Gong Date: Wed, 19 Jun 2024 16:29:19 +0800 Subject: [PATCH] IPEX Duplicate importer V2 (#11310) * Add gguf support. * Avoid error when import ipex-llm for multiple times. * Add check to avoid duplicate replace and revert. * Add calling from check to avoid raising exceptions in the submodule. * Add BIGDL_CHECK_DUPLICATE_IMPORT for controlling duplicate checker. Default is true. --- python/llm/src/ipex_llm/__init__.py | 6 +- .../llm/src/ipex_llm/transformers/convert.py | 3 + .../llm/src/ipex_llm/transformers/gguf/api.py | 3 + .../llm/src/ipex_llm/utils/ipex_importer.py | 67 ++++++++++++++++++- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/python/llm/src/ipex_llm/__init__.py b/python/llm/src/ipex_llm/__init__.py index a796dcbfb9d..1969aca0eb4 100644 --- a/python/llm/src/ipex_llm/__init__.py +++ b/python/llm/src/ipex_llm/__init__.py @@ -26,14 +26,16 @@ import sys import types -# Default is false, set to true to auto importing Intel Extension for PyTorch. +# Default is True, set to False to disable auto importing Intel Extension for PyTorch. USE_NPU = os.getenv("BIGDL_USE_NPU", 'False').lower() in ('true', '1', 't') BIGDL_IMPORT_IPEX = os.getenv("BIGDL_IMPORT_IPEX", 'True').lower() in ('true', '1', 't') BIGDL_IMPORT_IPEX = not USE_NPU and BIGDL_IMPORT_IPEX if BIGDL_IMPORT_IPEX: # Import Intel Extension for PyTorch as ipex if XPU version is installed from .utils.ipex_importer import ipex_importer - ipex_importer.import_ipex() + # Avoid duplicate import + if ipex_importer.get_ipex_version() is None: + ipex_importer.import_ipex() # Default is true, set to true to auto patching bigdl-llm to ipex_llm. BIGDL_COMPATIBLE_MODE = os.getenv("BIGDL_COMPATIBLE_MODE", 'True').lower() in ('true', '1', 't') diff --git a/python/llm/src/ipex_llm/transformers/convert.py b/python/llm/src/ipex_llm/transformers/convert.py index ccb309df132..cac49836548 100644 --- a/python/llm/src/ipex_llm/transformers/convert.py +++ b/python/llm/src/ipex_llm/transformers/convert.py @@ -773,6 +773,9 @@ def ggml_convert_low_bit(model, qtype, optimize_model=True, f"{list(gguf_mixed_qtype.keys())[index]} " f"format......") modules_to_not_convert = [] if modules_to_not_convert is None else modules_to_not_convert + # Disable ipex duplicate import checker + from ipex_llm.utils.ipex_importer import revert_import + revert_import() # using ipex_llm optimizer before changing to bigdl linear _enable_ipex = get_enable_ipex() diff --git a/python/llm/src/ipex_llm/transformers/gguf/api.py b/python/llm/src/ipex_llm/transformers/gguf/api.py index 05203fe0bda..95ebba3b57a 100644 --- a/python/llm/src/ipex_llm/transformers/gguf/api.py +++ b/python/llm/src/ipex_llm/transformers/gguf/api.py @@ -30,6 +30,9 @@ def load_gguf_model(fpath: str, dtype: torch.dtype = torch.float, low_bit: str = "sym_int4"): from .gguf import GGUFFileLoader + # Disable ipex duplicate import checker + from ipex_llm.utils.ipex_importer import revert_import + revert_import() loader = GGUFFileLoader(fpath) model_family = loader.config["general.architecture"] diff --git a/python/llm/src/ipex_llm/utils/ipex_importer.py b/python/llm/src/ipex_llm/utils/ipex_importer.py index 3fb543af6d4..aa02b15f802 100644 --- a/python/llm/src/ipex_llm/utils/ipex_importer.py +++ b/python/llm/src/ipex_llm/utils/ipex_importer.py @@ -18,15 +18,73 @@ import logging import builtins import sys -from ipex_llm.utils.common import log4Error +import os import inspect +from ipex_llm.utils.common import log4Error -# Save the original __import__ function -original_import = builtins.__import__ + +# Default is True, set to False to disable IPEX duplicate checker +BIGDL_CHECK_DUPLICATE_IMPORT = os.getenv("BIGDL_CHECK_DUPLICATE_IMPORT", + 'True').lower() in ('true', '1', 't') +RAW_IMPORT = None +IS_IMPORT_REPLACED = False ipex_duplicate_import_error = "intel_extension_for_pytorch has already been automatically " + \ "imported. Please avoid importing it again!" +def replace_import(): + global RAW_IMPORT, IS_IMPORT_REPLACED + # Avoid multiple replacement + if not IS_IMPORT_REPLACED and RAW_IMPORT is None: + # Save the original __import__ function + RAW_IMPORT = builtins.__import__ + builtins.__import__ = custom_ipex_import + IS_IMPORT_REPLACED = True + + +def revert_import(): + if not BIGDL_CHECK_DUPLICATE_IMPORT: + return + global RAW_IMPORT, IS_IMPORT_REPLACED + # Only revert once + if RAW_IMPORT is not None and IS_IMPORT_REPLACED: + builtins.__import__ = RAW_IMPORT + IS_IMPORT_REPLACED = False + + +def get_calling_package(): + """ + Return calling package name, e.g., ipex_llm.transformers + """ + # Get the current stack frame + frame = inspect.currentframe() + # Get the caller's frame + caller_frame = frame.f_back.f_back + # Get the caller's module + module = inspect.getmodule(caller_frame) + if module: + # Return the module's package name + return module.__package__ + return None + + +def custom_ipex_import(name, globals=None, locals=None, fromlist=(), level=0): + """ + Custom import function to avoid importing ipex again + """ + if fromlist is not None or '.' in name: + return RAW_IMPORT(name, globals, locals, fromlist, level) + # Avoid errors in submodule import + calling = get_calling_package() + if calling is not None: + return RAW_IMPORT(name, globals, locals, fromlist, level) + # Only check ipex for main thread + if name == "ipex" or name == "intel_extension_for_pytorch": + log4Error.invalidInputError(False, + ipex_duplicate_import_error) + return RAW_IMPORT(name, globals, locals, fromlist, level) + + class IPEXImporter: """ Auto import Intel Extension for PyTorch as ipex, @@ -71,6 +129,9 @@ def import_ipex(self): ipex_duplicate_import_error) self.directly_import_ipex() self.ipex_version = ipex.__version__ + # Replace builtin import to avoid duplicate ipex import + if BIGDL_CHECK_DUPLICATE_IMPORT: + replace_import() logging.info("intel_extension_for_pytorch auto imported") def directly_import_ipex(self):