code: def allreduce(data, op, prepare_fun=None):
      
      if not isinstance(data, np.ndarray):
          raise Exception('allreduce only takes in numpy.ndarray')
      buf = data.ravel()
      if buf.base is data.base:
          buf = buf.copy()
      if buf.dtype not in DTYPE_ENUM__:
          raise Exception('data type %s not supported' % str(buf.dtype))
      if prepare_fun is None:
          _LIB.RabitAllreduce(buf.ctypes.data_as(ctypes.c_void_p),
                              buf.size, DTYPE_ENUM__[buf.dtype],
                              op, None, None)
      else:
          func_ptr = ctypes.CFUNCTYPE(None, ctypes.c_void_p)
          def pfunc(args):
              """prepare function."""
              prepare_fun(data)
          _LIB.RabitAllreduce(buf.ctypes.data_as(ctypes.c_void_p),
                              buf.size, DTYPE_ENUM__[buf.dtype],
                              op, func_ptr(pfunc), None)
      return buf
docs: Perform allreduce, return the result.
  
      Parameters
      ----------
      data: numpy array
          Input data.
      op: int
          Reduction operators, can be MIN, MAX, SUM, BITOR
      prepare_fun: function
          Lazy preprocessing function, if it is not None, prepare_fun(data)
          will be called by the function before performing allreduce, to intialize the data
          If the result of Allreduce can be recovered directly,
          then prepare_fun will NOT be called
  
      Returns
      -------
      result : array_like
          The result of allreduce, have same shape as data
  
      Notes
      -----
      This function is not thread-safe.