class_docstr: The execution engine middleware of Cross-Graph Optimization (CGO).
  It's a technique that merges multiple models into one model for training speedup.
  See `Retiarii paper <https://www.usenix.org/system/files/osdi20-zhang_quanlu.pdf>`__ for details.
  
  Currently, :class:`CrossGraphOptimization` is only a prototype.
  It's not fully tested, and also, comes with a bunch of constraints on the model space and evaluator:
  
  - The models must be in the format of :class:`~nni.nas.space.GraphModelSpace`.
  - The evaluator has to be a :class:`~nni.nas.evaluator.pytorch.Lightning` evaluator.
  - The ``lightning_module`` argument of the evaluator must be an instance of
    :class:`~nni.nas.execution.cgo.evaluator.MultiModelSupervisedLearningModule`.
  - The ``trainer`` argument of the evaluator must be an instance of
    :class:`~nni.nas.execution.cgo.evaluator.MultiModelTrainer`.
  
  There are also a number of limitations:
  
  - CGO doesn't support stop and resume a checkpoint.
  - Only remote training service is supported.
  - All model history are stored in memory. The experiment might not scale well.
  
  Parameters
  ----------
  remote_config
      The remote training service config.
  max_concurrency
      The maximum number of trials to run concurrently.
  batch_waiting_time
      Seconds to wait for each batch of trial submission.
      The trials within one batch could apply cross-graph optimization.
class_docstr_tok: ["The","execution","engine","middleware","of","Cross-Graph","Optimization","(","CGO",")",".","It","'s","a","technique","that","merges","multiple","models","into","one","model","for","training","speedup",".","See","`","Retiarii","paper","<","https",":","\/\/www.usenix.org\/system\/files\/osdi20-zhang_quanlu.pdf",">","`","__","for","details",".","Currently",",",":","class",":","`","CrossGraphOptimization","`","is","only","a","prototype",".","It","'s","not","fully","tested",",","and","also",",","comes","with","a","bunch","of","constraints","on","the","model","space","and","evaluator",":","-","The","models","must","be","in","the","format","of",":","class",":","`","~nni.nas.space.GraphModelSpace","`",".","-","The","evaluator","has","to","be","a",":","class",":","`","~nni.nas.evaluator.pytorch.Lightning","`","evaluator",".","-","The","``","lightning_module","``","argument","of","the","evaluator","must","be","an","instance","of",":","class",":","`","~nni.nas.execution.cgo.evaluator.MultiModelSupervisedLearningModule","`",".","-","The","``","trainer","``","argument","of","the","evaluator","must","be","an","instance","of",":","class",":","`","~nni.nas.execution.cgo.evaluator.MultiModelTrainer","`",".","There","are","also","a","number","of","limitations",":","-","CGO","does","n't","support","stop","and","resume","a","checkpoint",".","-","Only","remote","training","service","is","supported",".","-","All","model","history","are","stored","in","memory",".","The","experiment","might","not","scale","well",".","Parameters","--","--","--","--","--","remote_config","The","remote","training","service","config",".","max_concurrency","The","maximum","number","of","trials","to","run","concurrently",".","batch_waiting_time","Seconds","to","wait","for","each","batch","of","trial","submission",".","The","trials","within","one","batch","could","apply","cross-graph","optimization","."]
code: def shutdown(self):
      self._stopped = True
      self._consumer_thread.join()
  
      if self._engine is None:
          _logger.warning(
              "Underlying engine is not set. Skip shutdown."
          )
  
      else:
          self.engine.unregister_model_event_callback(
              ModelEventType.TrainingEnd, self._training_end_callback
          )
          self.engine.unregister_model_event_callback(
              ModelEventType.FinalMetric, self._final_metric_callback
          )
          self.engine.unregister_model_event_callback(
              ModelEventType.IntermediateMetric,
              self._intermediate_metric_callback,
          )
  
          self.engine.shutdown()
  
imports: from __future__ import annotations
  import logging
  import time
  import threading
  from collections.abc import Iterable
  from typing import List, Dict, Tuple, cast
  from nni.common.device import GPUDevice, Device
  from nni.experiment.config.training_services import RemoteConfig
  from nni.nas.space import GraphModelSpace, Node, ModelStatus
  from nni.nas.execution.engine import Middleware, ExecutionEngine
  from nni.nas.execution.event import ModelEventType, IntermediateMetricEvent, FinalMetricEvent, TrainingEndEvent
  from nni.typehint import TrialMetric
  from .logical_optimizer.logical_plan import LogicalPlan, AbstractLogicalNode
  from .logical_optimizer.opt_dedup_input import DedupInputOptimizer
label_desc: Use image node_id 3 for calling the CrossGraphOptimization obj's underlying member method code with example usage: obj.shutdown() without return types