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 _device_partition(
      sharding_option: ShardingOption,
      minheap_devices: List[OrderedDeviceHardware],
      bulk_heapify_threshold: float = 0.25,
  ) -> None:
      pushlimit = len(minheap_devices) * bulk_heapify_threshold
      for shard in sharding_option.shards:
          tmp_heap = []
          while minheap_devices:
              ordered_device = minheap_devices[0]
              device = ordered_device.device
              storage = cast(Storage, shard.storage)
              if storage.fits_in(device.storage):
                  shard.rank = device.rank
                  device.storage -= cast(Storage, shard.storage)
                  device.perf += cast(Perf, shard.perf)
                  heapq.heapreplace(minheap_devices, ordered_device)
                  break
              else:
                  heapq.heappop(minheap_devices)
                  tmp_heap.append(ordered_device)
          else:
              raise PlannerError(
                  error_type=PlannerErrorType.PARTITION,
                  message=(
                      f"Device partition failed. Couldn't find a rank for shard {shard} of table {sharding_option.name}, "
                      f"largest device storage: {max(ordered_device.device.storage for ordered_device in tmp_heap)}"
                  ),
              )
          if tmp_heap:
              # restablish minheap
              if len(tmp_heap) <= pushlimit:
                  for ordered_device in tmp_heap:
                      heapq.heappush(minheap_devices, ordered_device)
              else:
                  minheap_devices.extend(tmp_heap)
                  heapq.heapify(minheap_devices)
  
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 4 for calling the GreedyPerfPartitioner obj's underlying member method code with example usage: obj._device_partition(sharding_option, minheap_devices, bulk_heapify_threshold) without return types