class_docstr: Greedy Partitioner
  
  Args:
      sort_by (SortBy): Sort sharding options by storage or perf in
          descending order (i.e., large tables will be placed first).
      balance_modules (bool): Whether to sort by modules first, where
          smaller modules will be sorted first. In effect, this will place
          tables in each module in a balanced way.
code: def _cohost_partition(
      sharding_option_group: ShardingOptionGroup,
      _host_level_devices: List[List[DeviceHardware]],
  ) -> None:
      sorted_host_level_devices = _sort_devices_by_perf(
          _host_level_devices
      )
      for devices in sorted_host_level_devices:
          host_devices = copy.deepcopy(devices)
          host_storage = Storage(hbm=0, ddr=0)
          for device in host_devices:
              host_storage += device.storage
          if not sharding_option_group.storage_sum.fits_in(
              host_storage
          ):
              continue
  
          success = True
          minheap_devices: Optional[List[OrderedDeviceHardware]] = None
          for sharding_option in sharding_option_group.sharding_options:
              try:
                  if (
                      sharding_option.sharding_type
                      == ShardingType.TABLE_ROW_WISE.value
                  ):
                      GreedyPerfPartitioner._uniform_partition(
                          [sharding_option], host_devices
                      )
                      # _uniform_partition invalidates minheap_devices, force rebuild
                      # before using
                      minheap_devices = None
                  elif (
                      sharding_option.sharding_type
                      == ShardingType.TABLE_COLUMN_WISE.value
                  ):
                      if minheap_devices is None:
                          minheap_devices = (
                              GreedyPerfPartitioner._establish_minheap(
                                  host_devices, len(host_devices)
                              )
                          )
                      GreedyPerfPartitioner._device_partition(
                          sharding_option, minheap_devices
                      )
                  else:
                      raise RuntimeError(
                          f"unexpected cohost sharding type: {sharding_option.sharding_type}"
                      )
              except PlannerError:
                  success = False
                  break
          if success:
              # successfully found a host and partitioned on that host
              # need to update the devices
              # resorting host_devices before copying data back
              host_devices.sort(key=lambda device: device.rank)
              for device, device_copy in zip(devices, host_devices):
                  device.storage = device_copy.storage
                  device.perf = device_copy.perf
              return
      raise PlannerError(
          error_type=PlannerErrorType.PARTITION,
          message=f"can't find a host for sharding option group {sharding_option_group}",
      )
  
imports: import copy
  import heapq
  import logging
  from dataclasses import dataclass
  from enum import Enum
  from typing import cast, Dict, List, Optional
  from torchrec.distributed.planner.perf_models import NoopPerfModel
  from torchrec.distributed.planner.types import DeviceHardware, PartitionByType, Partitioner, Perf, PerfModel, PlannerError, PlannerErrorType, ShardingOption, Storage, Topology
  from torchrec.distributed.planner.utils import bytes_to_gb, reset_shard_rank
  from torchrec.distributed.types import ShardingType
label_desc: Use image node_id 5 for calling the GreedyPerfPartitioner obj's underlying member method code with example usage: obj._cohost_partition(sharding_option_group, _host_level_devices) without return types