class_docstr: Given arrays *values* and *keys* of equal length
  and a number *nkeys* of keys, returns a tuple `(starts,
  lists)`, as follows: *values* and *keys* are sorted
  by *keys*, and the sorted *values* is returned as
  *lists*. Then for each index *i* in ``range(nkeys)``,
  *starts[i]* is written to indicating where the
  group of *values* belonging to the key with index
  *i* begins. It implicitly ends at *starts[i+1]*.
  
  ``starts`` is built so that it has ``nkeys + 1`` entries, so that
  the *i*'th entry is the start of the *i*'th list, and the
  *i*'th entry is the index one past the *i*'th list's end,
  even for the last list.
  
  This implies that all lists are contiguous.
  
  .. note:: This functionality is provided as a preview. Its
      interface is subject to change until this notice is removed.
  
  .. versionadded:: 2013.1
code: def __call__(
      self,
      queue,
      keys,
      values,
      nkeys,
      starts_dtype,
      allocator=None,
      wait_for=None,
  ):
      if allocator is None:
          allocator = values.allocator
  
      knl_info = self.get_kernels(
          keys.dtype, values.dtype, starts_dtype
      )
  
      (
          values_sorted_by_key,
          keys_sorted_by_key,
      ), evt = knl_info.by_target_sorter(
          values, keys, queue=queue, wait_for=wait_for
      )
  
      starts = cl.array.empty(
          queue, (nkeys + 1), starts_dtype, allocator=allocator
      ).fill(len(values_sorted_by_key), wait_for=[evt])
      (evt,) = starts.events
  
      evt = knl_info.start_finder(
          starts,
          keys_sorted_by_key,
          range=slice(len(keys_sorted_by_key)),
          wait_for=[evt],
      )
  
      evt = knl_info.bound_propagation_scan(
          starts, nkeys, queue=queue, wait_for=[evt]
      )
  
      return starts, values_sorted_by_key, evt
  
imports: from dataclasses import dataclass
  from typing import Optional
  import numpy
  import pyopencl
  import pyopencl.array
  from pyopencl.elementwise import ElementwiseKernel
  from pyopencl.scan import ScanTemplate, GenericScanKernel
  from pyopencl.tools import dtype_to_ctype, get_arg_offset_adjuster_code
  from pytools import memoize, memoize_method
  from mako.template import Template
label_desc: Use image node_id 3 for calling the KeyValueSorter obj's underlying member method code with example usage: obj.__call__(queue, keys, values, nkeys, starts_dtype, allocator, wait_for) and returns: starts,  values_sorted_by_key,  evt