This is an automated email from the ASF dual-hosted git repository.

junrushao pushed a commit to branch unity
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/unity by this push:
     new 35e8404f17 [Disco] Expose `DiscoWorker` and `ndarray_cache_support` in 
header (#16153)
35e8404f17 is described below

commit 35e8404f17a2aae2e00110748ecce515395a5428
Author: Lesheng Jin <[email protected]>
AuthorDate: Sun Dec 10 18:25:40 2023 +0800

    [Disco] Expose `DiscoWorker` and `ndarray_cache_support` in header (#16153)
---
 {src => include/tvm}/runtime/disco/builtin.h       |  38 ++++-
 .../tvm/runtime/disco/disco_worker.h               |  71 +--------
 include/tvm/runtime/disco/session.h                |  20 ++-
 .../tvm}/runtime/relax_vm/ndarray_cache_support.h  |  39 ++---
 python/tvm/relax/frontend/nn/core.py               |   2 +
 python/tvm/relax/frontend/nn/op.py                 |  39 +++++
 python/tvm/runtime/disco/process_pool.py           |  16 +-
 python/tvm/runtime/disco/session.py                |   3 +-
 src/runtime/disco/bcast_session.h                  |   3 +-
 src/runtime/disco/builtin.cc                       |   5 +-
 src/runtime/disco/{worker.cc => disco_worker.cc}   |   8 +-
 src/runtime/disco/disco_worker_thread.h            |  83 +++++++++++
 src/runtime/disco/loader.cc                        |  89 ++++++++++--
 src/runtime/disco/nccl/nccl.cc                     |   5 +-
 src/runtime/disco/process_session.cc               |   9 +-
 src/runtime/disco/session.cc                       |   3 +-
 src/runtime/disco/threaded_session.cc              |   3 +-
 src/runtime/disco/utils.h                          |  32 +---
 src/runtime/relax_vm/ndarray_cache_support.cc      | 161 ++++++++++-----------
 19 files changed, 379 insertions(+), 250 deletions(-)

diff --git a/src/runtime/disco/builtin.h b/include/tvm/runtime/disco/builtin.h
similarity index 81%
rename from src/runtime/disco/builtin.h
rename to include/tvm/runtime/disco/builtin.h
index cfbf2e2477..3847aef3f2 100644
--- a/src/runtime/disco/builtin.h
+++ b/include/tvm/runtime/disco/builtin.h
@@ -25,11 +25,37 @@
 
 #include <string>
 
-#include "./utils.h"
-
 namespace tvm {
 namespace runtime {
 
+/*!
+ * \brief Possible kinds of reduction operations.
+ */
+enum class ReduceKind : int32_t {
+  kSum = 0,
+  kProd = 1,
+  kMin = 2,
+  kMax = 3,
+  kAvg = 4,
+};
+
+/*! \brief Converts `ReduceKind` to string */
+inline std::string ReduceKind2String(ReduceKind kind) {
+  switch (kind) {
+    case ReduceKind::kSum:
+      return "kSum";
+    case ReduceKind::kProd:
+      return "kProd";
+    case ReduceKind::kMin:
+      return "kMin";
+    case ReduceKind::kMax:
+      return "kMax";
+    case ReduceKind::kAvg:
+      return "kAvg";
+  }
+  LOG(FATAL) << "ValueError: Unknown ReduceKind: " << static_cast<int>(kind);
+}
+
 /*!
  * \brief Load a runtime Module, then create and initialize a RelaxVM
  * \param path The path to the runtime Module (a DSO file) to be loaded
@@ -49,19 +75,19 @@ NDArray DiscoEmptyNDArray(ShapeTuple shape, DataType dtype, 
Device device);
  * \brief Perform an allreduce operation using the underlying communication 
library
  * \param send The array send to perform allreduce on
  * \param reduce_kind The kind of reduction operation (e.g. sum, avg, min, max)
- * \return The outcome of allreduce
+ * \param recv The array receives the outcome of allreduce
  */
 void AllReduce(NDArray send, ReduceKind reduce_kind, NDArray recv);
 /*!
  * \brief Perform an allgather operation using the underlying communication 
library
  * \param send The array send to perform allgather on
- * \return The outcome of allgather
+ * \param recv The array receives the outcome of allgather
  */
 void AllGather(NDArray send, NDArray recv);
 /*!
  * \brief Perform a broadcast operation from worker-0
- * \param buffer The buffer to be broadcasted
- * \return The result buffer
+ * \param send The buffer to be broadcasted
+ * \param recv The buffer receives the broadcasted array
  */
 void BroadcastFromWorker0(NDArray send, NDArray recv);
 /*!
diff --git a/src/runtime/disco/worker.h 
b/include/tvm/runtime/disco/disco_worker.h
similarity index 60%
rename from src/runtime/disco/worker.h
rename to include/tvm/runtime/disco/disco_worker.h
index e948fa1668..0c666150d4 100644
--- a/src/runtime/disco/worker.h
+++ b/include/tvm/runtime/disco/disco_worker.h
@@ -17,44 +17,22 @@
  * under the License.
  */
 /*!
- * \file worker.h
+ * \file disco_worker.h
  * \brief This file defines a worker in Disco. A worker can be launched in a 
separate thread or
  * process as long as the channel supports bi-directional communication 
in-between the worker and
  * the controler.
  */
-#ifndef TVM_RUNTIME_DISCO_WORKER_H_
-#define TVM_RUNTIME_DISCO_WORKER_H_
+#ifndef TVM_RUNTIME_DISCO_DISCO_WORKER_H_
+#define TVM_RUNTIME_DISCO_DISCO_WORKER_H_
 
 #include <tvm/runtime/disco/session.h>
 #include <tvm/runtime/packed_func.h>
 
-#include <atomic>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <utility>
 #include <vector>
 
 namespace tvm {
 namespace runtime {
 
-/*!
- * \brief A special communication channel between controler and worker-0,
- * assuming they are always collocated in the same process.
- */
-class WorkerZeroData {
- public:
-  /*!
-   * \brief The host-side arrays to passed to worker-0 for special uses, for 
example,
-   * copy-to-worker0 and copy-from-worker0
-   */
-  std::queue<NDArray> host_arrays;
-  /*! \brief The mutex that guards `host_arrays` */
-  std::mutex queue_mutex_;
-};
-
 /*!
  * \brief A worker in Disco. It takes a channel to communication with the 
controler.
  * The worker can be run in a separate thread or process as long as the 
channel supports
@@ -65,6 +43,7 @@ class DiscoWorker {
   /*!
    * \brief Construct a worker.
    * \param worker_id The id of the worker.
+   * \param num_workers The number of the workers.
    * \param worker_zero_data The data shared between worker-0 and the 
controler. It's a nullptr if
    * the worker is not worker-0.
    * \param channel The communication channel between the worker and the 
controler.
@@ -111,46 +90,6 @@ class DiscoWorker {
   friend struct DiscoWorker::Impl;
 };
 
-/*!
- * \brief A worker thread in Disco, which upon creation, launches a new thread 
to run the
- * DiscoWorker.
- * \sa DiscoWorker
- */
-class DiscoWorkerThread {
- public:
-  /*!
-   * \brief Construct a worker thread.
-   * \param worker_id The id of the worker.
-   * \param num_workers The total number of workers.
-   * \param worker_zero_data_ The data shared between worker-0 and the 
controler. It's a nullptr if
-   * the worker is not worker-0.
-   */
-  explicit DiscoWorkerThread(int worker_id, int num_workers, WorkerZeroData* 
worker_zero_data_);
-
-  /*! \brief Move constructor. */
-  explicit DiscoWorkerThread(DiscoWorkerThread&& other)
-      : channel(std::move(other.channel)),
-        worker(std::move(other.worker)),
-        thread(std::move(other.thread)) {}
-
-  /*! \brief Copy constructor is disabled */
-  DiscoWorkerThread(const DiscoWorkerThread& other) = delete;
-
-  /*! \brief Destructor that joins the thread before destruction */
-  ~DiscoWorkerThread() {
-    if (this->thread != nullptr) {
-      this->thread->join();
-    }
-  }
-
-  /*! \brief The communication channel between the controler and the worker */
-  std::unique_ptr<DiscoChannel> channel;
-  /*! \brief The worker whose internal state is visible to the controler */
-  std::unique_ptr<DiscoWorker> worker;
-  /*! \brief The thread that runs the worker's main loop. */
-  std::unique_ptr<std::thread> thread;
-};
-
 }  // namespace runtime
 }  // namespace tvm
-#endif  // TVM_RUNTIME_DISCO_WORKER_H_
+#endif  // TVM_RUNTIME_DISCO_DISCO_WORKER_H_
diff --git a/include/tvm/runtime/disco/session.h 
b/include/tvm/runtime/disco/session.h
index 3de519e9cc..5e745166b0 100644
--- a/include/tvm/runtime/disco/session.h
+++ b/include/tvm/runtime/disco/session.h
@@ -76,6 +76,7 @@
 #include <tvm/runtime/object.h>
 #include <tvm/runtime/packed_func.h>
 
+#include <queue>
 #include <string>
 #include <utility>
 
@@ -270,10 +271,12 @@ class Session : public ObjectRef {
    * and returns a PackedFunc, which takes an integer `worker_id` as the input 
and returns None.
    * When `worker-id` is 0, it shuts down the process pool; Otherwise, it 
retursn a tuple
    * (read_fd, writefd) used to communicate with the corresponding worker.
+   * \param entrypoint The entrypoint of DiscoWorker main worker function.
    * \note Worker-0 is always co-located with the controler as a separate 
thread, and therefore
    * worker-0 does not exist in the process pool.
    */
-  TVM_DLL static Session ProcessSession(int num_workers, String 
process_pool_creator);
+  TVM_DLL static Session ProcessSession(int num_workers, String 
process_pool_creator,
+                                        String entrypoint);
   TVM_DEFINE_MUTABLE_NOTNULLABLE_OBJECT_REF_METHODS(Session, ObjectRef, 
SessionObj);
 };
 
@@ -294,6 +297,21 @@ class DiscoChannel {
   virtual TVMArgs RecvReply() = 0;
 };
 
+/*!
+ * \brief A special communication channel between controler and worker-0,
+ * assuming they are always collocated in the same process.
+ */
+class WorkerZeroData {
+ public:
+  /*!
+   * \brief The host-side arrays to passed to worker-0 for special uses, for 
example,
+   * copy-to-worker0 and copy-from-worker0
+   */
+  std::queue<NDArray> host_arrays;
+  /*! \brief The mutex that guards `host_arrays` */
+  std::mutex queue_mutex_;
+};
+
 // Implementation details
 
 DRefObj::~DRefObj() {
diff --git a/src/runtime/relax_vm/ndarray_cache_support.h 
b/include/tvm/runtime/relax_vm/ndarray_cache_support.h
similarity index 77%
rename from src/runtime/relax_vm/ndarray_cache_support.h
rename to include/tvm/runtime/relax_vm/ndarray_cache_support.h
index c1beb5a946..3d8b639ee4 100644
--- a/src/runtime/relax_vm/ndarray_cache_support.h
+++ b/include/tvm/runtime/relax_vm/ndarray_cache_support.h
@@ -42,10 +42,11 @@ struct NDArrayCacheMetadata {
        * \brief Load the parameter from raw data.
        * \param device The device to load the parameter onto.
        * \param raw_data The raw data stream
-       * \param f_load The function to load the parameter from raw data.
+       * \param staging_buffer The buffer to be used to avoid extra OpenCL 
copies. Pass in a nullptr
+       * in other cases
        */
       NDArray Load(Device device, const std::string* raw_data,
-                   std::function<void(NDArray, const void*, int64_t)> f_load) 
const;
+                   Optional<NDArray>* staging_buffer = nullptr) const;
 
       /*! \brief Name of the parameter */
       std::string name;
@@ -61,6 +62,12 @@ struct NDArrayCacheMetadata {
       int64_t byte_offset;
     };
 
+    /*! \brief Load a FileRecord into memory */
+    Array<NDArray> Load(Device device,                   //
+                        const std::string& path_prefix,  //
+                        std::string* raw_data_buffer,    //
+                        Optional<NDArray>* staging_buffer = nullptr) const;
+
     /*! \brief Relative path to the bin file */
     std::string data_path;
     /*! \brief Format of the file */
@@ -75,34 +82,12 @@ struct NDArrayCacheMetadata {
   /*! \brief The path to the `ndarray-cache.json` file */
   std::string path;
 
-  /*! \brief Load the metadata from a specific path */
+  /*! \brief Load the metadata from a specific directory */
+  static NDArrayCacheMetadata Load(const std::string& path);
+  /*! \brief Load the metadata from a given JSON string */
   static NDArrayCacheMetadata LoadFromStr(const std::string& json_str, const 
std::string& path);
 };
 
-/*!
- * \brief Information of sharding function,
- * including the shard function name and extra parameters.
- */
-struct ShardInfo {
-  struct TensorInfo {
-    ShapeTuple shape;
-    DataType dtype;
-  };
-  struct ShardFunc {
-    std::string name;
-    TensorInfo output_info;
-    std::vector<int64_t> params;
-  };
-  std::vector<ShardFunc> funcs;
-};
-
-/*!
- * \brief Load the shard information from dist
- * \param path Path to the file to be loaded
- * \return Mapping from parameter name to its shard dim
- */
-std::unordered_map<std::string, ShardInfo> LoadShardInfoFromStr(const 
std::string& json_str);
-
 }  // namespace relax_vm
 }  // namespace runtime
 }  // namespace tvm
diff --git a/python/tvm/relax/frontend/nn/core.py 
b/python/tvm/relax/frontend/nn/core.py
index ba48b449fc..c7d745c721 100644
--- a/python/tvm/relax/frontend/nn/core.py
+++ b/python/tvm/relax/frontend/nn/core.py
@@ -176,6 +176,7 @@ class Parameter(Tensor):
     """
 
     _data: Optional[NDArray]
+    attrs: Dict[str, Any]
 
     def __init__(
         self,
@@ -196,6 +197,7 @@ class Parameter(Tensor):
             dtype = get_default_dtype()
         super().__init__(_expr=_tensor_placeholder("param", shape, 
dtype=dtype)._expr)
         self._data = None
+        self.attrs = OrderedDict()
 
     @property
     def data(self) -> Optional[NDArray]:
diff --git a/python/tvm/relax/frontend/nn/op.py 
b/python/tvm/relax/frontend/nn/op.py
index b95ceac4ed..76af0044c3 100644
--- a/python/tvm/relax/frontend/nn/op.py
+++ b/python/tvm/relax/frontend/nn/op.py
@@ -1372,6 +1372,45 @@ def interpolate(
     )
 
 
+def ccl_allreduce(x: Tensor, op_type: str = "sum", name="ccl_allreduce"):
+    """CCL Allreduce operator
+
+    Parameters
+    ----------
+    x : Tensor
+      The input tensor.
+    op_type: str
+      The type of reduction operation to be applied to the input data.
+      Now "sum", "prod", "min", "max" and "avg" are supported.
+    name : str
+        Name hint for this operation.
+
+    Returns
+    -------
+    result : Tensor
+      The result tensor of allreduce.
+    """
+    return _wrap_nested(_op.ccl.allreduce(x._expr, op_type), name)
+
+
+def ccl_broadcast_from_worker0(x: Tensor, name="broadcast_from_worker"):
+    """Broadcast data from worker-0 to all other workers.
+
+    Parameters
+    ----------
+    x : Tensor
+      The tensor to be broadcast.
+    name : str
+        Name hint for this operation.
+
+    Returns
+    -------
+    result : Tensor
+      The same tensor, which has been broadcast to all other workers.
+    """
+    return _wrap_nested(_op.ccl.broadcast_from_worker0(x._expr), name)
+
+
 def tensor_expr_op(
     tensor_expr_func: Callable,
     name_hint: str,
diff --git a/python/tvm/runtime/disco/process_pool.py 
b/python/tvm/runtime/disco/process_pool.py
index 836744dba6..e91d855953 100644
--- a/python/tvm/runtime/disco/process_pool.py
+++ b/python/tvm/runtime/disco/process_pool.py
@@ -47,9 +47,17 @@ class DiscoPopenWorker:
         The standard error streams handler specified for the popen process.
     """
 
-    def __init__(self, worker_id: int, num_workers: int, stdout=None, 
stderr=None):
+    def __init__(  # pylint: disable=too-many-arguments
+        self,
+        worker_id: int,
+        num_workers: int,
+        entrypoint: str = "tvm.exec.disco_worker",
+        stdout=None,
+        stderr=None,
+    ):
         self.worker_id = worker_id
         self.num_workers = num_workers
+        self.entrypoint = entrypoint
         self._proc = None
         self._stdout = stdout
         self._stderr = stderr
@@ -109,7 +117,7 @@ class DiscoPopenWorker:
         cmd = [
             sys.executable,
             "-m",
-            "tvm.exec.disco_worker",
+            self.entrypoint,
             str(self.worker_id),
             str(self.num_workers),
         ]
@@ -164,9 +172,9 @@ def _kill_child_processes(pid):
 
 
 @register_func("runtime.disco.create_process_pool")
-def _create_process_pool(num_workers: int):
+def _create_process_pool(num_workers: int, entrypoint: str):
     """Create a process pool where the workers' are [1, num_workers)."""
-    pool = [DiscoPopenWorker(i, num_workers) for i in range(1, num_workers)]
+    pool = [DiscoPopenWorker(i, num_workers, entrypoint) for i in range(1, 
num_workers)]
 
     def result_func(worker_id: int):
         nonlocal pool
diff --git a/python/tvm/runtime/disco/session.py 
b/python/tvm/runtime/disco/session.py
index a7defd96cd..8d0ff57a32 100644
--- a/python/tvm/runtime/disco/session.py
+++ b/python/tvm/runtime/disco/session.py
@@ -360,10 +360,11 @@ class ThreadedSession(Session):
 class ProcessSession(Session):
     """A Disco session backed by pipe-based multi-processing."""
 
-    def __init__(self, num_workers: int) -> None:
+    def __init__(self, num_workers: int, entrypoint: str) -> None:
         self.__init_handle_by_constructor__(
             _ffi_api.SessionProcess,  # type: ignore # pylint: 
disable=no-member
             num_workers,
+            entrypoint,
             "runtime.disco.create_process_pool",
         )
 
diff --git a/src/runtime/disco/bcast_session.h 
b/src/runtime/disco/bcast_session.h
index 772e14c087..1a4df634b7 100644
--- a/src/runtime/disco/bcast_session.h
+++ b/src/runtime/disco/bcast_session.h
@@ -19,13 +19,12 @@
 #ifndef TVM_RUNTIME_DISCO_BCAST_SESSION_H_
 #define TVM_RUNTIME_DISCO_BCAST_SESSION_H_
 
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/disco/session.h>
 
 #include <string>
 #include <vector>
 
-#include "./worker.h"
-
 namespace tvm {
 namespace runtime {
 
diff --git a/src/runtime/disco/builtin.cc b/src/runtime/disco/builtin.cc
index 514d633fa6..51fe4c13fc 100644
--- a/src/runtime/disco/builtin.cc
+++ b/src/runtime/disco/builtin.cc
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include "./builtin.h"
-
 #include <dlpack/dlpack.h>
 #include <tvm/runtime/container/shape_tuple.h>
+#include <tvm/runtime/disco/builtin.h>
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/disco/session.h>
 #include <tvm/runtime/packed_func.h>
 #include <tvm/runtime/registry.h>
@@ -28,7 +28,6 @@
 #include <sstream>
 
 #include "./utils.h"
-#include "./worker.h"
 
 namespace tvm {
 namespace runtime {
diff --git a/src/runtime/disco/worker.cc b/src/runtime/disco/disco_worker.cc
similarity index 98%
rename from src/runtime/disco/worker.cc
rename to src/runtime/disco/disco_worker.cc
index 9192215dda..d3c6d6a383 100644
--- a/src/runtime/disco/worker.cc
+++ b/src/runtime/disco/disco_worker.cc
@@ -16,17 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#include "./worker.h"
-
-#include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/disco/builtin.h>
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/disco/session.h>
 #include <tvm/runtime/packed_func.h>
 #include <tvm/runtime/registry.h>
 
-#include <thread>
-
 #include "../../support/process_id.h"
-#include "./builtin.h"
 #include "./protocol.h"
 
 namespace tvm {
diff --git a/src/runtime/disco/disco_worker_thread.h 
b/src/runtime/disco/disco_worker_thread.h
new file mode 100644
index 0000000000..67742cdd04
--- /dev/null
+++ b/src/runtime/disco/disco_worker_thread.h
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*!
+ * \file disco_worker_thread.h
+ * \brief This file defines a worker in Disco. A worker can be launched in a 
separate thread or
+ * process as long as the channel supports bi-directional communication 
in-between the worker and
+ * the controler.
+ */
+#ifndef TVM_RUNTIME_DISCO_DISCO_WORKER_THREAD_H_
+#define TVM_RUNTIME_DISCO_DISCO_WORKER_THREAD_H_
+
+#include <tvm/runtime/disco/disco_worker.h>
+#include <tvm/runtime/disco/session.h>
+#include <tvm/runtime/packed_func.h>
+
+#include <memory>
+#include <thread>
+#include <utility>
+
+namespace tvm {
+namespace runtime {
+
+/*!
+ * \brief A worker thread in Disco, which upon creation, launches a new thread 
to run the
+ * DiscoWorker.
+ * \sa DiscoWorker
+ */
+class DiscoWorkerThread {
+ public:
+  /*!
+   * \brief Construct a worker thread.
+   * \param worker_id The id of the worker.
+   * \param num_workers The total number of workers.
+   * \param worker_zero_data_ The data shared between worker-0 and the 
controler. It's a nullptr if
+   * the worker is not worker-0.
+   * \note This method is implemented in threaded worker, because it depends 
on creation of a
+   * sub-class of DiscoChannel, DiscoThreadChannel, which is hidden from the 
public interface.
+   */
+  explicit DiscoWorkerThread(int worker_id, int num_workers, WorkerZeroData* 
worker_zero_data_);
+
+  /*! \brief Move constructor. */
+  explicit DiscoWorkerThread(DiscoWorkerThread&& other)
+      : channel(std::move(other.channel)),
+        worker(std::move(other.worker)),
+        thread(std::move(other.thread)) {}
+
+  /*! \brief Copy constructor is disabled */
+  DiscoWorkerThread(const DiscoWorkerThread& other) = delete;
+
+  /*! \brief Destructor that joins the thread before destruction */
+  ~DiscoWorkerThread() {
+    if (this->thread != nullptr) {
+      this->thread->join();
+    }
+  }
+
+  /*! \brief The communication channel between the controler and the worker */
+  std::unique_ptr<DiscoChannel> channel;
+  /*! \brief The worker whose internal state is visible to the controler */
+  std::unique_ptr<DiscoWorker> worker;
+  /*! \brief The thread that runs the worker's main loop. */
+  std::unique_ptr<std::thread> thread;
+};
+
+}  // namespace runtime
+}  // namespace tvm
+#endif  // TVM_RUNTIME_DISCO_DISCO_WORKER_THREAD_H_
diff --git a/src/runtime/disco/loader.cc b/src/runtime/disco/loader.cc
index c931baa942..7a5d978946 100644
--- a/src/runtime/disco/loader.cc
+++ b/src/runtime/disco/loader.cc
@@ -16,9 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+#define PICOJSON_USE_INT64
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <picojson.h>
 #include <tvm/runtime/data_type.h>
+#include <tvm/runtime/disco/builtin.h>
 #include <tvm/runtime/packed_func.h>
 #include <tvm/runtime/registry.h>
+#include <tvm/runtime/relax_vm/ndarray_cache_support.h>
 
 #include <functional>
 #include <numeric>
@@ -27,8 +34,6 @@
 #include <vector>
 
 #include "../file_utils.h"
-#include "../relax_vm/ndarray_cache_support.h"
-#include "./builtin.h"
 #include "./utils.h"
 
 namespace tvm {
@@ -37,7 +42,75 @@ namespace runtime {
 using relax_vm::NDArrayCacheMetadata;
 using FileRecord = NDArrayCacheMetadata::FileRecord;
 using ParamRecord = NDArrayCacheMetadata::FileRecord::ParamRecord;
-using relax_vm::ShardInfo;
+
+struct ShardInfo {
+  struct TensorInfo {
+    ShapeTuple shape;
+    DataType dtype;
+  };
+  struct ShardFunc {
+    std::string name;
+    TensorInfo output_info;
+    std::vector<int64_t> params;
+  };
+  std::vector<ShardFunc> funcs;
+};
+
+template <typename ExpectedType>
+inline ExpectedType AsType(const picojson::value& json) {
+  ICHECK(json.is<ExpectedType>());
+  return json.get<ExpectedType>();
+}
+
+template <typename ValueType>
+inline ValueType GetValue(const picojson::object& json, const std::string& 
key) {
+  return AsType<ValueType>(json.at(key));
+}
+
+std::unordered_map<std::string, ShardInfo> LoadShardInfoFromStr(const 
std::string& json_str);
+ShardInfo::TensorInfo LoadTensorInfoFromJSON(const picojson::array& 
json_tensor_info) {
+  CHECK_EQ(json_tensor_info.size(), 2) << "ValueError: Invalid tensor info 
JSON";
+  picojson::array shape_json = AsType<picojson::array>(json_tensor_info[0]);
+  int ndim = shape_json.size();
+  std::vector<int64_t> shape;
+  shape.reserve(ndim);
+  for (int i = 0; i < ndim; ++i) {
+    shape.push_back(AsType<int64_t>(shape_json[i]));
+  }
+  std::string dtype = AsType<std::string>(json_tensor_info[1]);
+  return ShardInfo::TensorInfo{ShapeTuple(std::move(shape)), 
DataType(String2DLDataType(dtype))};
+}
+
+ShardInfo::ShardFunc LoadShardFuncFromJSON(const picojson::array& 
json_shard_func) {
+  int n = json_shard_func.size();
+  ShardInfo::ShardFunc shard_info;
+  shard_info.name = AsType<std::string>(json_shard_func[0]);
+  shard_info.output_info = 
LoadTensorInfoFromJSON(AsType<picojson::array>(json_shard_func[1]));
+  shard_info.params.reserve(n - 2);
+  for (int i = 2; i < n; ++i) {
+    shard_info.params.push_back(AsType<int64_t>(json_shard_func[i]));
+  }
+  return shard_info;
+}
+
+std::unordered_map<std::string, ShardInfo> LoadShardInfoFromStr(const 
std::string& json_str) {
+  picojson::value json_info;
+  picojson::parse(json_info, json_str);
+  picojson::object json_obj = AsType<picojson::object>(json_info);
+  std::unordered_map<std::string, ShardInfo> result;
+  for (auto kv : json_obj) {
+    std::string name = kv.first;
+    picojson::array json_shard_funcs = AsType<picojson::array>(kv.second);
+    ShardInfo info;
+    std::vector<ShardInfo::ShardFunc>& shard_funcs = info.funcs;
+    shard_funcs.reserve(json_shard_funcs.size());
+    for (const picojson::value& json_shard_func : json_shard_funcs) {
+      
shard_funcs.push_back(LoadShardFuncFromJSON(AsType<picojson::array>(json_shard_func)));
+    }
+    result[name] = info;
+  }
+  return result;
+}
 
 /*! \brief An object that helps to load parameters in shards. */
 class ShardLoaderObj : public Object {
@@ -114,7 +187,7 @@ ObjectRef ShardLoaderObj::Create(const std::string& 
path_to_metadata, const std:
   n->metadata_ = NDArrayCacheMetadata::LoadFromStr(metadata, path_to_metadata);
   n->current_file_ = nullptr;
   n->param_info_.clear();
-  std::unordered_map<std::string, ShardInfo> shards = 
relax_vm::LoadShardInfoFromStr(shard_info);
+  std::unordered_map<std::string, ShardInfo> shards = 
LoadShardInfoFromStr(shard_info);
   for (const FileRecord& file_record : n->metadata_.records) {
     for (const ParamRecord& param_record : file_record.records) {
       const std::string& name = param_record.name;
@@ -181,9 +254,7 @@ NDArray ShardLoaderObj::LoadParamOnWorker0(int 
weight_index) const {
       std::string file_name = GetSiblingPath(this->metadata_.path, 
file->data_path);
       LoadBinaryFromFile(file_name, &this->current_file_stream_);
     }
-    return param->Load(
-        device, &this->current_file_stream_,
-        [](NDArray param, const void* data, size_t nbytes) { 
param.CopyFromBytes(data, nbytes); });
+    return param->Load(device, &this->current_file_stream_);
   };
 
   if (worker_id == 0) {
@@ -230,9 +301,7 @@ NDArray ShardLoaderObj::LoadDirect(int weight_index) const {
     std::string file_name = GetSiblingPath(this->metadata_.path, 
file->data_path);
     LoadBinaryFromFile(file_name, &this->current_file_stream_);
   }
-  return param->Load(
-      device, &this->current_file_stream_,
-      [](NDArray param, const void* data, size_t nbytes) { 
param.CopyFromBytes(data, nbytes); });
+  return param->Load(device, &this->current_file_stream_);
 }
 
 NDArray ShardLoaderObj::Load(int weight_index) const {
diff --git a/src/runtime/disco/nccl/nccl.cc b/src/runtime/disco/nccl/nccl.cc
index e61306377f..61c307c673 100644
--- a/src/runtime/disco/nccl/nccl.cc
+++ b/src/runtime/disco/nccl/nccl.cc
@@ -19,6 +19,7 @@
 
 #include <dlpack/dlpack.h>
 #include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/disco/builtin.h>
 #include <tvm/runtime/disco/session.h>
 #include <tvm/runtime/registry.h>
 
@@ -336,10 +337,10 @@ TVM_REGISTER_GLOBAL("runtime.disco." TVM_DISCO_CCL_NAME 
".init_ccl_per_worker")
 TVM_REGISTER_GLOBAL("runtime.disco." TVM_DISCO_CCL_NAME ".allreduce")
     .set_body_typed([](NDArray send, int kind, NDArray recv) {
       CHECK(0 <= kind && kind <= 4) << "ValueError: Unknown ReduceKind: " << 
kind;
-      AllReduce(send, static_cast<ReduceKind>(kind), recv);
+      nccl::AllReduce(send, static_cast<ReduceKind>(kind), recv);
     });
 TVM_REGISTER_GLOBAL("runtime.disco." TVM_DISCO_CCL_NAME ".allgather")
-    .set_body_typed([](NDArray send, NDArray recv) { AllGather(send, recv); });
+    .set_body_typed([](NDArray send, NDArray recv) { nccl::AllGather(send, 
recv); });
 TVM_REGISTER_GLOBAL("runtime.disco." TVM_DISCO_CCL_NAME 
".broadcast_from_worker0")
     .set_body_typed(BroadcastFromWorker0);
 TVM_REGISTER_GLOBAL("runtime.disco." TVM_DISCO_CCL_NAME 
".scatter_from_worker0")
diff --git a/src/runtime/disco/process_session.cc 
b/src/runtime/disco/process_session.cc
index ece93d07bf..467a635181 100644
--- a/src/runtime/disco/process_session.cc
+++ b/src/runtime/disco/process_session.cc
@@ -16,6 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+#include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/object.h>
 #include <tvm/runtime/packed_func.h>
 #include <tvm/runtime/registry.h>
@@ -28,9 +30,8 @@
 #include "../../support/pipe.h"
 #include "../minrpc/rpc_reference.h"
 #include "./bcast_session.h"
+#include "./disco_worker_thread.h"
 #include "./protocol.h"
-#include "./worker.h"
-#include "tvm/runtime/c_runtime_api.h"
 
 namespace tvm {
 namespace runtime {
@@ -191,11 +192,11 @@ class ProcessSessionObj final : public BcastSessionObj {
 TVM_REGISTER_OBJECT_TYPE(DiscoDebugObject);
 TVM_REGISTER_OBJECT_TYPE(ProcessSessionObj);
 
-Session Session::ProcessSession(int num_workers, String process_pool_creator) {
+Session Session::ProcessSession(int num_workers, String process_pool_creator, 
String entrypoint) {
   const PackedFunc* pf = Registry::Get(process_pool_creator);
   CHECK(pf) << "ValueError: Cannot find function " << process_pool_creator
             << " in the registry. Please check if it is registered.";
-  PackedFunc process_pool = (*pf)(num_workers);
+  PackedFunc process_pool = (*pf)(num_workers, entrypoint);
   auto n = make_object<ProcessSessionObj>(num_workers, process_pool);
   return Session(n);
 }
diff --git a/src/runtime/disco/session.cc b/src/runtime/disco/session.cc
index c588af82f1..12339c4fa5 100644
--- a/src/runtime/disco/session.cc
+++ b/src/runtime/disco/session.cc
@@ -16,12 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/disco/session.h>
 #include <tvm/runtime/packed_func.h>
 #include <tvm/runtime/registry.h>
 
-#include "./worker.h"
-
 namespace tvm {
 namespace runtime {
 
diff --git a/src/runtime/disco/threaded_session.cc 
b/src/runtime/disco/threaded_session.cc
index 349601fd03..985601aeb6 100644
--- a/src/runtime/disco/threaded_session.cc
+++ b/src/runtime/disco/threaded_session.cc
@@ -18,6 +18,7 @@
  */
 #include <dmlc/io.h>
 #include <tvm/runtime/c_runtime_api.h>
+#include <tvm/runtime/disco/disco_worker.h>
 #include <tvm/runtime/object.h>
 
 #include <condition_variable>
@@ -30,8 +31,8 @@
 #include "../../support/ring_buffer.h"
 #include "../minrpc/rpc_reference.h"
 #include "./bcast_session.h"
+#include "./disco_worker_thread.h"
 #include "./protocol.h"
-#include "./worker.h"
 
 namespace tvm {
 namespace runtime {
diff --git a/src/runtime/disco/utils.h b/src/runtime/disco/utils.h
index ace3873a38..0c177e36e9 100644
--- a/src/runtime/disco/utils.h
+++ b/src/runtime/disco/utils.h
@@ -20,12 +20,10 @@
 #define TVM_RUNTIME_DISCO_UTILS_H_
 
 #include <dlpack/dlpack.h>
-#include <tvm/runtime/disco/session.h>
+#include <tvm/runtime/disco/disco_worker.h>
 
 #include <string>
 
-#include "./worker.h"
-
 namespace tvm {
 namespace runtime {
 
@@ -36,34 +34,6 @@ inline Device UseDefaultDeviceIfNone(Device device) {
   return device;
 }
 
-/*!
- * \brief Possible kinds of reduction operations.
- */
-enum class ReduceKind : int32_t {
-  kSum = 0,
-  kProd = 1,
-  kMin = 2,
-  kMax = 3,
-  kAvg = 4,
-};
-
-/*! \brief Converts `ReduceKind` to string */
-inline std::string ReduceKind2String(ReduceKind kind) {
-  switch (kind) {
-    case ReduceKind::kSum:
-      return "kSum";
-    case ReduceKind::kProd:
-      return "kProd";
-    case ReduceKind::kMin:
-      return "kMin";
-    case ReduceKind::kMax:
-      return "kMax";
-    case ReduceKind::kAvg:
-      return "kAvg";
-  }
-  LOG(FATAL) << "ValueError: Unknown ReduceKind: " << static_cast<int>(kind);
-}
-
 /*!
  * \brief Converts a 1-d shape tuple to an integer.
  * \note At the time of scaffolding Disco, RelaxVM has not provided mature 
support for standalone
diff --git a/src/runtime/relax_vm/ndarray_cache_support.cc 
b/src/runtime/relax_vm/ndarray_cache_support.cc
index 25f1fd282e..613c70bb44 100644
--- a/src/runtime/relax_vm/ndarray_cache_support.cc
+++ b/src/runtime/relax_vm/ndarray_cache_support.cc
@@ -38,11 +38,10 @@
 #ifndef __STDC_FORMAT_MACROS
 #define __STDC_FORMAT_MACROS
 #endif
-#include "./ndarray_cache_support.h"
-
 #include <picojson.h>
 #include <tvm/runtime/ndarray.h>
 #include <tvm/runtime/registry.h>
+#include <tvm/runtime/relax_vm/ndarray_cache_support.h>
 
 #include <string>
 #include <vector>
@@ -55,13 +54,13 @@ namespace runtime {
 namespace relax_vm {
 
 template <typename ExpectedType>
-ExpectedType AsType(const picojson::value& json) {
+inline ExpectedType AsType(const picojson::value& json) {
   ICHECK(json.is<ExpectedType>());
   return json.get<ExpectedType>();
 }
 
 template <typename ValueType>
-ValueType GetValue(const picojson::object& json, const std::string& key) {
+inline ValueType GetValue(const picojson::object& json, const std::string& 
key) {
   return AsType<ValueType>(json.at(key));
 }
 
@@ -111,59 +110,63 @@ NDArrayCacheMetadata JSONAsNDArrayCacheMetadata(const 
picojson::object& json) {
 NDArrayCacheMetadata NDArrayCacheMetadata::LoadFromStr(const std::string& 
json_str,
                                                        const std::string& 
path) {
   picojson::value json_info;
-  picojson::parse(json_info, json_str);
+  {
+    std::string err = picojson::parse(json_info, json_str);
+    if (!err.empty()) {
+      LOG(FATAL) << "Failed to parse JSON: err. The JSON string is:" << 
json_str;
+    }
+    CHECK(json_info.is<picojson::object>())
+        << "ValueError: The given string is not a JSON object: " << json_str;
+  }
   NDArrayCacheMetadata result = 
JSONAsNDArrayCacheMetadata(AsType<picojson::object>(json_info));
   result.path = path;
   return result;
 }
 
-ShardInfo::TensorInfo LoadTensorInfoFromJSON(const picojson::array& 
json_tensor_info) {
-  CHECK_EQ(json_tensor_info.size(), 2) << "ValueError: Invalid tensor info 
JSON";
-  picojson::array shape_json = AsType<picojson::array>(json_tensor_info[0]);
-  int ndim = shape_json.size();
-  std::vector<int64_t> shape;
-  shape.reserve(ndim);
-  for (int i = 0; i < ndim; ++i) {
-    shape.push_back(AsType<int64_t>(shape_json[i]));
+NDArrayCacheMetadata NDArrayCacheMetadata::Load(const std::string& path) {
+  picojson::value json_info;
+  {
+    std::string json_str;
+    LoadBinaryFromFile(path + "/ndarray-cache.json", &json_str);
+    std::string err = picojson::parse(json_info, json_str);
+    if (!err.empty()) {
+      LOG(FATAL) << "Failed to parse JSON: err. The JSON string is:" << 
json_str;
+    }
+    CHECK(json_info.is<picojson::object>())
+        << "ValueError: The given string is not a JSON object: " << json_str;
   }
-  std::string dtype = AsType<std::string>(json_tensor_info[1]);
-  return ShardInfo::TensorInfo{ShapeTuple(std::move(shape)), 
DataType(String2DLDataType(dtype))};
+  NDArrayCacheMetadata result = 
JSONAsNDArrayCacheMetadata(AsType<picojson::object>(json_info));
+  result.path = path;
+  return result;
 }
 
-ShardInfo::ShardFunc LoadShardFuncFromJSON(const picojson::array& 
json_shard_func) {
-  int n = json_shard_func.size();
-  ShardInfo::ShardFunc shard_info;
-  shard_info.name = AsType<std::string>(json_shard_func[0]);
-  shard_info.output_info = 
LoadTensorInfoFromJSON(AsType<picojson::array>(json_shard_func[1]));
-  shard_info.params.reserve(n - 2);
-  for (int i = 2; i < n; ++i) {
-    shard_info.params.push_back(AsType<int64_t>(json_shard_func[i]));
+void CopyNDArrayFromBytes(NDArray param, const void* data, size_t nbytes,
+                          Optional<NDArray>* staging_buffer) {
+  Device device = param->device;
+  if (device.device_type != kDLOpenCL || staging_buffer == nullptr) {
+    param.CopyFromBytes(data, nbytes);
+    return;
   }
-  return shard_info;
-}
-
-std::unordered_map<std::string, ShardInfo> LoadShardInfoFromStr(const 
std::string& json_str) {
-  picojson::value json_info;
-  picojson::parse(json_info, json_str);
-  picojson::object json_obj = AsType<picojson::object>(json_info);
-  std::unordered_map<std::string, ShardInfo> result;
-  for (auto kv : json_obj) {
-    std::string name = kv.first;
-    picojson::array json_shard_funcs = AsType<picojson::array>(kv.second);
-    ShardInfo info;
-    std::vector<ShardInfo::ShardFunc>& shard_funcs = info.funcs;
-    shard_funcs.reserve(json_shard_funcs.size());
-    for (const picojson::value& json_shard_func : json_shard_funcs) {
-      
shard_funcs.push_back(LoadShardFuncFromJSON(AsType<picojson::array>(json_shard_func)));
+  // Special handle for OpenCL runtime.
+  // It creates a host side memory mirror, for every cl_mem that tries to copy 
data from host
+  // which can cause memory issue. Her we use a large staging buffer to 
postpone deallocation
+  if (staging_buffer->defined()) {
+    size_t curr_size = 
runtime::GetDataSize(*(staging_buffer->value().operator->()));
+    if (curr_size < nbytes) {
+      *staging_buffer = NullOpt;
     }
-    result[name] = info;
   }
-  return result;
+  if (!staging_buffer->defined()) {
+    *staging_buffer = NDArray::Empty(param.Shape(), param->dtype, 
param->device);
+  }
+  NDArray staging_view = staging_buffer->value().CreateView(param.Shape(), 
param->dtype);
+  staging_view.CopyFromBytes(data, nbytes);
+  param.CopyFrom(staging_view);
+  TVMSynchronize(device.device_type, device.device_id, nullptr);
 }
 
 NDArray NDArrayCacheMetadata::FileRecord::ParamRecord::Load(
-    Device device, const std::string* raw_data,
-    std::function<void(NDArray, const void*, int64_t)> f_load) const {
+    Device device, const std::string* raw_data, Optional<NDArray>* 
staging_buffer) const {
   NDArray arr = NDArray::Empty(shape, dtype, device);
   if (dtype == DataType::Float(32) && format == "f32-to-bf16") {
     // decode bf16 to f32
@@ -173,13 +176,30 @@ NDArray 
NDArrayCacheMetadata::FileRecord::ParamRecord::Load(
     for (size_t i = 0; i < buffer.size(); ++i) {
       decoded[i] = static_cast<uint32_t>(buffer[i]) << 16;
     }
-    f_load(arr, decoded.data(), decoded.size() * sizeof(uint32_t));
+    CopyNDArrayFromBytes(arr, decoded.data(), decoded.size() * 
sizeof(uint32_t), staging_buffer);
   } else {
-    f_load(arr, raw_data->data() + byte_offset, nbytes);
+    CopyNDArrayFromBytes(arr, raw_data->data() + byte_offset, nbytes, 
staging_buffer);
   }
   return arr;
 }
 
+Array<NDArray> NDArrayCacheMetadata::FileRecord::Load(Device device,
+                                                      const std::string& 
path_prefix,  //
+                                                      std::string* 
raw_data_buffer,    //
+                                                      Optional<NDArray>* 
staging_buffer) const {
+  LoadBinaryFromFile(path_prefix + "/" + this->data_path, raw_data_buffer);
+  CHECK_EQ(this->format, "raw-shard") << "ValueError: Only `raw-shard` format 
is supported";
+  CHECK_EQ(this->nbytes, raw_data_buffer->length())
+      << "ValueError: Encountered an corrupted parameter shard. It means it is 
not downloaded "
+         "completely or downloading is interrupted. Please try to download 
again.";
+  Array<NDArray> result;
+  result.reserve(this->records.size());
+  for (const ParamRecord& nd_rec : this->records) {
+    result.push_back(nd_rec.Load(device, raw_data_buffer, staging_buffer));
+  }
+  return result;
+}
+
 /*!
  * A NDArray cache to store pre-loaded arrays in the system.
  */
@@ -217,53 +237,26 @@ class NDArrayCache {
 
   /*!
    * \brief Load parameters from path and append them.
-   *
    * \param cache_path The cache to path.
    * \param device_type The type of device to be loaded.
    * \param device_id The device id.
    */
   static void Load(const std::string& cache_path, int device_type, int 
device_id) {
     DLDevice device{static_cast<DLDeviceType>(device_type), device_id};
-    std::string json_str;
-    LoadBinaryFromFile(cache_path + "/ndarray-cache.json", &json_str);
-    NDArrayCacheMetadata metadata = 
NDArrayCacheMetadata::LoadFromStr(json_str, cache_path);
+    NDArrayCacheMetadata metadata = NDArrayCacheMetadata::Load(cache_path);
     Optional<NDArray> staging_buffer;
-    auto fcopy_param_from_bytes = [&](NDArray param, const void* data, size_t 
nbytes) {
-      if (device_type != kDLOpenCL) {
-        param.CopyFromBytes(data, nbytes);
-        return;
-      }
-      // special handle OpenCL
-      // OpenCL runtime can create a host side memory mirror
-      // for every cl_mem that tries to copy data from host
-      // which can cause memory issue.
-      // We use a single staging buffer here
-      // that get de-allocated later
-      if (staging_buffer.defined()) {
-        size_t curr_size = 
runtime::GetDataSize(*(staging_buffer.value().operator->()));
-        if (curr_size < nbytes) {
-          staging_buffer = NullOpt;
-        }
-      }
-      if (!staging_buffer.defined()) {
-        staging_buffer = NDArray::Empty(param.Shape(), param->dtype, 
param->device);
-      }
-      NDArray staging_view = staging_buffer.value().CreateView(param.Shape(), 
param->dtype);
-      staging_view.CopyFromBytes(data, nbytes);
-      param.CopyFrom(staging_view);
-      TVMSynchronize(device_type, device_id, nullptr);
-    };
-
-    Map<String, NDArray> result;
     std::string raw_data;
-    for (const auto& shard_rec : metadata.records) {
-      LoadBinaryFromFile(cache_path + "/" + shard_rec.data_path, &raw_data);
-      CHECK_EQ(shard_rec.format, "raw-shard") << "ValueError: Only `raw-shard` 
format is supported";
-      CHECK_EQ(shard_rec.nbytes, raw_data.length())
-          << "ValueError: Parameters are not loaded properly. Please check 
your parameter shards "
-             "and git lfs installation";
-      for (const auto& nd_rec : shard_rec.records) {
-        Update(nd_rec.name, nd_rec.Load(device, &raw_data, 
fcopy_param_from_bytes), true);
+    Array<NDArray> params;
+    for (const NDArrayCacheMetadata::FileRecord& shard_rec : metadata.records) 
{
+      try {
+        params = shard_rec.Load(device, cache_path, &raw_data, 
&staging_buffer);
+      } catch (const dmlc::Error& e) {
+        LOG(FATAL) << "ValueError: Error when loading parameters from " << 
shard_rec.data_path
+                   << ": " << e.what();
+      }
+      int num_params = params.size();
+      for (int i = 0; i < num_params; ++i) {
+        Update(shard_rec.records[i].name, params[i], true);
       }
     }
   }


Reply via email to