executor_base.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import asyncio
  2. from abc import ABC, abstractmethod
  3. from typing import List, Optional, Set, Tuple
  4. from aphrodite.common.config import (CacheConfig, DeviceConfig, LoadConfig,
  5. LoRAConfig, ModelConfig, ParallelConfig,
  6. SchedulerConfig, SpeculativeConfig,
  7. VisionLanguageConfig)
  8. from aphrodite.common.sequence import ExecuteModelRequest, SamplerOutput
  9. from aphrodite.lora.request import LoRARequest
  10. class ExecutorBase(ABC):
  11. """Base class for all executors.
  12. An executor is responsible for executing the model on a specific device
  13. type (e.g., CPU, GPU, Neuron, etc.). Or it can be a distributed executor
  14. that can execute the model on multiple devices.
  15. """
  16. def __init__(
  17. self,
  18. model_config: ModelConfig,
  19. cache_config: CacheConfig,
  20. parallel_config: ParallelConfig,
  21. scheduler_config: SchedulerConfig,
  22. device_config: DeviceConfig,
  23. load_config: LoadConfig,
  24. lora_config: Optional[LoRAConfig],
  25. vision_language_config: Optional[VisionLanguageConfig],
  26. speculative_config: Optional[SpeculativeConfig],
  27. ) -> None:
  28. self.model_config = model_config
  29. self.cache_config = cache_config
  30. self.lora_config = lora_config
  31. self.load_config = load_config
  32. self.parallel_config = parallel_config
  33. self.scheduler_config = scheduler_config
  34. self.device_config = device_config
  35. self.vision_language_config = vision_language_config
  36. self.speculative_config = speculative_config
  37. self._init_executor()
  38. @abstractmethod
  39. def _init_executor(self) -> None:
  40. pass
  41. @abstractmethod
  42. def determine_num_available_blocks(self) -> Tuple[int, int]:
  43. """Determine the number of available blocks for the GPU KV cache and
  44. swappable CPU KV cache.
  45. Normally, this should simply delegate to the underlying Worker. Some
  46. ExecutorBase may require modification of the result, e.g. to ensure the
  47. selected cache sizes are compatible with all workers.
  48. Returns a Tuple[num_gpu_blocks, num_cpu_blocks], where num_gpu_blocks
  49. are blocks that are "active" on the device and can be appended to.
  50. num_cpu_blocks refers to "swapped" blocks in CPU memory and cannot be
  51. appended to.
  52. """
  53. raise NotImplementedError
  54. @abstractmethod
  55. def initialize_cache(self, num_gpu_blocks: int,
  56. num_cpu_blocks: int) -> None:
  57. """Initialize the KV cache with the given size in blocks.
  58. """
  59. raise NotImplementedError
  60. @abstractmethod
  61. def execute_model(
  62. self, execute_model_req: ExecuteModelRequest
  63. ) -> Optional[List[SamplerOutput]]:
  64. """Executes at least one model step on the given sequences."""
  65. raise NotImplementedError
  66. def stop_remote_worker_execution_loop(self) -> None:
  67. """Releases parallel workers from model loop."""
  68. return
  69. @abstractmethod
  70. def add_lora(self, lora_request: LoRARequest) -> bool:
  71. raise NotImplementedError
  72. @abstractmethod
  73. def remove_lora(self, lora_id: int) -> bool:
  74. raise NotImplementedError
  75. @abstractmethod
  76. def list_loras(self) -> Set[int]:
  77. raise NotImplementedError
  78. @abstractmethod
  79. def pin_lora(self, lora_id: int) -> bool:
  80. raise NotImplementedError # type: ignore
  81. @abstractmethod
  82. def check_health(self) -> None:
  83. """Checks if the executor is healthy. If not, it should raise an
  84. exception."""
  85. raise NotImplementedError
  86. def shutdown(self) -> None:
  87. """Shutdown the executor."""
  88. return
  89. def __del__(self):
  90. self.shutdown()
  91. class ExecutorAsyncBase(ExecutorBase):
  92. def __init__(
  93. self,
  94. model_config: ModelConfig,
  95. cache_config: CacheConfig,
  96. parallel_config: ParallelConfig,
  97. scheduler_config: SchedulerConfig,
  98. device_config: DeviceConfig,
  99. load_config: LoadConfig,
  100. lora_config: Optional[LoRAConfig],
  101. vision_language_config: Optional[VisionLanguageConfig],
  102. speculative_config: Optional[SpeculativeConfig],
  103. ) -> None:
  104. # This locks each pipeline parallel stage so multiple virtual engines
  105. # can't execute on the same stage at the same time
  106. self.pp_locks = [
  107. asyncio.Lock()
  108. for _ in range(parallel_config.pipeline_parallel_size)
  109. ]
  110. super().__init__(model_config, cache_config, parallel_config,
  111. scheduler_config, device_config, load_config,
  112. lora_config, vision_language_config,
  113. speculative_config)
  114. @abstractmethod
  115. async def execute_model_async(
  116. self,
  117. execute_model_req: ExecuteModelRequest) -> List[SamplerOutput]:
  118. """Executes one model step on the given sequences."""
  119. raise NotImplementedError
  120. async def stop_remote_worker_execution_loop_async(self) -> None:
  121. """Releases parallel workers from model loop."""
  122. return
  123. async def check_health_async(self) -> None:
  124. """Checks if the executor is healthy. If not, it should raise an
  125. exception."""
  126. self.check_health()