code: def why_not_local_access_conflict_free(
      dev, itemsize, array_shape, array_stored_shape=None
  ):
      """
      :param itemsize: size of accessed data in bytes
      :param array_shape: array dimensions, fastest-moving last
          (C order)
  
      :returns: a tuple (multiplicity, explanation), where *multiplicity*
          is the number of work items that will conflict on a bank when accessing
          local memory. *explanation* is a string detailing the found conflict.
      """
      # FIXME: Treat 64-bit access on NV CC 2.x + correctly
  
      if array_stored_shape is None:
          array_stored_shape = array_shape
  
      rank = len(array_shape)
  
      array_shape = array_shape[::-1]
      array_stored_shape = array_stored_shape[::-1]
  
      gran = local_memory_access_granularity(dev)
      if itemsize != gran:
          from warnings import warn
  
          warn(
              f"Local conflict info might be inaccurate for itemsize != {gran}",
              CLCharacterizationWarning,
              stacklevel=2,
          )
  
      sim_wi = simultaneous_work_items_on_local_access(dev)
      bank_count = local_memory_bank_count(dev)
  
      conflicts = []
  
      for work_item_axis in range(rank):
          bank_accesses = {}
          for work_item_id in range(sim_wi):
              addr = 0
              addr_mult = itemsize
  
              idx = []
              left_over_idx = work_item_id
              for axis, (ax_size, ax_stor_size) in enumerate(
                  zip(array_shape, array_stored_shape)
              ):
                  if axis >= work_item_axis:
                      left_over_idx, ax_idx = divmod(
                          left_over_idx, ax_size
                      )
                      addr += addr_mult * ax_idx
                      idx.append(ax_idx)
                  else:
                      idx.append(0)
  
                  addr_mult *= ax_stor_size
  
              if left_over_idx:
                  # out-of-bounds, assume not taking place
                  continue
  
              bank = (addr // gran) % bank_count
              bank_accesses.setdefault(bank, []).append(
                  "w.item {} -> {}".format(work_item_id, idx[::-1])
              )
  
          conflict_multiplicity = max(
              len(acc) for acc in bank_accesses.values()
          )
  
          if conflict_multiplicity > 1:
              for bank, acc in bank_accesses.items():
                  if len(acc) == conflict_multiplicity:
                      conflicts.append(
                          (
                              conflict_multiplicity,
                              "%dx conflict on axis %d (from right, 0-based): "
                              "%s access bank %d"
                              % (
                                  conflict_multiplicity,
                                  work_item_axis,
                                  ", ".join(acc),
                                  bank,
                              ),
                          )
                      )
  
      if conflicts:
          return max(conflicts)
      else:
          return 1, None
  
imports: import pyopencl
  from pytools import memoize
  from typing import Dict, Optional, Tuple
label_desc: Use image node_id 9 for calling a global function with example usage: why_not_local_access_conflict_free(dev, itemsize, array_shape, array_stored_shape) and returns: max, int,  None