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

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


The following commit(s) were added to refs/heads/main by this push:
     new 40b6c14bba [Disco] Add NVSHMEM support (#17317)
40b6c14bba is described below

commit 40b6c14bba2ae31d371644b33e261e4cbaaa5b54
Author: Yaxing Cai <[email protected]>
AuthorDate: Sun Sep 1 14:00:15 2024 -0700

    [Disco] Add NVSHMEM support (#17317)
    
    This PR adds the supports of NVSHMEM.
---
 CMakeLists.txt                         |  23 +++++++
 cmake/modules/LibInfo.cmake            |   1 +
 cmake/utils/FindNVSHMEM.cmake          |  52 +++++++++++++++
 src/runtime/contrib/nvshmem/nvshmem.cc |  66 +++++++++++++++++++
 src/support/libinfo.cc                 |   5 ++
 tests/python/disco/test_nvshmem.py     | 114 +++++++++++++++++++++++++++++++++
 6 files changed, 261 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index aa2a385683..38dd59b9c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ include(cmake/utils/FindLLVM.cmake)
 include(cmake/utils/FindROCM.cmake)
 include(cmake/utils/FindRCCL.cmake)
 include(cmake/utils/FindEthosN.cmake)
+include(cmake/utils/FindNVSHMEM.cmake)
 
 if(EXISTS ${CMAKE_BINARY_DIR}/config.cmake)
   include(${CMAKE_BINARY_DIR}/config.cmake)
@@ -133,6 +134,7 @@ tvm_option(USE_UMA "Build with UMA support" OFF)
 tvm_option(USE_VERILATOR "Build with Verilator support" OFF)
 tvm_option(USE_MSC "Enable Multi-System Compiler" OFF)
 tvm_option(USE_MRVL "Build with MRVL TVM support" OFF)
+tvm_option(USE_NVSHMEM "Build with NVSHMEM support" OFF)
 
 # include directories
 include_directories(${CMAKE_INCLUDE_PATH})
@@ -472,6 +474,16 @@ if(USE_CUDA AND USE_NCCL)
   list(APPEND RUNTIME_SRCS ${RUNTIME_NCCL_SRC})
 endif()
 
+if (USE_CUDA AND USE_NVSHMEM)
+  message(STATUS "Build with NVSHMEM...")
+  find_nvshmem(${USE_NVSHMEM})
+  if (NOT NVSHMEM_FOUND)
+    message(FATAL_ERROR "Cannot find NVSHMEM, USE_NVSHMEM=" ${USE_NVSHMEM})
+  endif()
+  tvm_file_glob(GLOB RUNTIME_NVSHMEM_SRCS src/runtime/contrib/nvshmem/*.cc)
+  list(APPEND RUNTIME_SRCS ${RUNTIME_NVSHMEM_SRCS})
+endif()
+
 if(USE_ROCM AND USE_RCCL)
   message(STATUS "Build with RCCL...")
   find_rccl(${USE_RCCL})
@@ -957,6 +969,17 @@ if(USE_CUDA AND USE_NCCL)
   target_link_libraries(tvm_runtime PRIVATE nccl ${LIBRT})
 endif()
 
+
+if (USE_CUDA AND USE_NVSHMEM)
+  include_directories(SYSTEM ${USE_NVSHMEM}/include)
+  find_library(NVSHMEM_HOST nvshmem_host ${NVSHMEM_LIB_DIR})
+  find_library(NVSHMEM_DEVICE nvshmem_device ${NVSHMEM_LIB_DIR})
+  target_link_libraries(tvm PRIVATE ${NVSHMEM_HOST} ${NVSHMEM_DEVICE})
+  target_link_libraries(tvm_runtime PRIVATE ${NVSHMEM_HOST} ${NVSHMEM_DEVICE})
+  set_target_properties(tvm PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+  set_target_properties(tvm_runtime PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+endif()
+
 if(USE_ROCM AND USE_RCCL)
   target_link_libraries(tvm PRIVATE rccl)
   target_link_libraries(tvm_runtime PRIVATE rccl)
diff --git a/cmake/modules/LibInfo.cmake b/cmake/modules/LibInfo.cmake
index da9bc3e1c9..a2b51bb331 100644
--- a/cmake/modules/LibInfo.cmake
+++ b/cmake/modules/LibInfo.cmake
@@ -143,6 +143,7 @@ function(add_lib_info src_file)
     TVM_INFO_USE_VERILATOR="${USE_VERILATOR}"
     TVM_INFO_USE_MSC="${USE_MSC}"
     TVM_INFO_USE_CCACHE="${USE_CCACHE}"
+    TVM_INFO_USE_NVSHMEM="${USE_NVSHMEM}"
     TVM_INFO_BACKTRACE_ON_SEGFAULT="${BACKTRACE_ON_SEGFAULT}"
   )
 
diff --git a/cmake/utils/FindNVSHMEM.cmake b/cmake/utils/FindNVSHMEM.cmake
new file mode 100644
index 0000000000..1a833332a2
--- /dev/null
+++ b/cmake/utils/FindNVSHMEM.cmake
@@ -0,0 +1,52 @@
+# 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.
+
+#######################################################
+# Enhanced version of find NVSHMEM.
+#
+# Usage:
+#   find_nvshmem(${USE_NVSHMEM})
+#
+# - When USE_NVSHMEM=ON, use auto search
+# - When USE_NVSHMEM=/path/to/installed/nvshmem, use the installed nvshmem 
path.
+#   Can be useful when nvshmem is installed at specified location.
+#
+# Provide variables:
+#
+# - NVSHMEM_FOUND
+# - NVSHMEM_INCLUDE_DIR
+# - NVSHMEM_LIB_DIR
+#
+
+macro(find_nvshmem use_nvshmem)
+  set(__use_nvshmem ${use_nvshmem})
+  if(IS_DIRECTORY ${__use_nvshmem})
+    set(__nvshmem_path ${__use_nvshmem})
+    message(STATUS "Custom NVSHMEM PATH=" ${__use_nvshmem})
+   elseif(IS_DIRECTORY $ENV{NVSHMEM_HOME})
+     set(__nvshmem_path $ENV{NVSHMEM_HOME})
+   else()
+     set(__nvshmem_path "")
+   endif()
+
+   find_package(NVSHMEM HINTS ${__nvshmem_path}/lib/cmake/nvshmem/)
+
+  if(NVSHMEM_FOUND)
+    message(STATUS "NVSHMEM_INCLUDE_DIR=" ${NVSHMEM_INCLUDE_DIR})
+    message(STATUS "NVSHMEM_LIB_DIR=" ${NVSHMEM_LIB_DIR})
+  endif(NVSHMEM_FOUND)
+endmacro(find_nvshmem)
diff --git a/src/runtime/contrib/nvshmem/nvshmem.cc 
b/src/runtime/contrib/nvshmem/nvshmem.cc
new file mode 100644
index 0000000000..985ba55107
--- /dev/null
+++ b/src/runtime/contrib/nvshmem/nvshmem.cc
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+#include <nvshmem.h>
+#include <nvshmemx.h>
+#include <tvm/runtime/disco/disco_worker.h>
+#include <tvm/runtime/packed_func.h>
+#include <tvm/runtime/registry.h>
+
+#include "../../cuda/cuda_common.h"
+
+namespace tvm {
+namespace runtime {
+
+ShapeTuple InitNVSHMEMUID() {
+  nvshmemx_uniqueid_t uid;
+  nvshmemx_get_uniqueid(&uid);
+  std::vector<int64_t> uid_64;
+  uid_64.push_back(static_cast<int64_t>(uid.version));
+  for (int i = 0; i < UNIQUEID_PADDING; ++i) {
+    uid_64.push_back(static_cast<int64_t>(uid.internal[i]));
+  }
+  return ShapeTuple(uid_64);
+}
+
+void InitNVSHMEM(ShapeTuple uid_64, int num_workers) {
+  DiscoWorker* worker = DiscoWorker::ThreadLocal();
+  ICHECK(worker != nullptr);
+  CHECK_EQ(uid_64.size(), UNIQUEID_PADDING + 1)
+      << "ValueError: The length of unique_id must be " << UNIQUEID_PADDING << 
", but got "
+      << uid_64.size() << ".";
+
+  nvshmemx_init_attr_t attr = NVSHMEMX_INIT_ATTR_INITIALIZER;
+
+  nvshmemx_uniqueid_t uid;
+  uid.version = static_cast<int>(uid_64[0]);
+  for (int i = 0; i < UNIQUEID_PADDING; ++i) {
+    uid.internal[i] = static_cast<char>(uid_64[i + 1]);
+  }
+  nvshmemx_set_attr_uniqueid_args(worker->worker_id, num_workers, &uid, &attr);
+  nvshmemx_init_attr(NVSHMEMX_INIT_WITH_UNIQUEID, &attr);
+  LOG_INFO << "NVSHMEM init finished: mype=" << nvshmem_my_pe() << " "
+           << ", npes=" << nvshmem_n_pes();
+}
+
+TVM_REGISTER_GLOBAL("runtime.disco.nvshmem.init_nvshmem_uid").set_body_typed(InitNVSHMEMUID);
+
+TVM_REGISTER_GLOBAL("runtime.disco.nvshmem.init_nvshmem").set_body_typed(InitNVSHMEM);
+
+}  // namespace runtime
+}  // namespace tvm
diff --git a/src/support/libinfo.cc b/src/support/libinfo.cc
index 984a2f3323..73800338b1 100644
--- a/src/support/libinfo.cc
+++ b/src/support/libinfo.cc
@@ -275,6 +275,10 @@
 #define TVM_INFO_USE_CCACHE "NOT-FOUND"
 #endif
 
+#ifndef TVM_INFO_USE_NVSHMEM
+#define TVM_INFO_USE_NVSHMEM "NOT-FOUND"
+#endif
+
 namespace tvm {
 
 /*!
@@ -387,6 +391,7 @@ TVM_DLL Map<String, String> GetLibInfo() {
       {"USE_VERILATOR", TVM_INFO_USE_VERILATOR},
       {"USE_MSC", TVM_INFO_USE_MSC},
       {"USE_CCACHE", TVM_INFO_USE_CCACHE},
+      {"USE_NVSHMEM", TVM_INFO_USE_NVSHMEM},
       {"BACKTRACE_ON_SEGFAULT", TVM_INFO_BACKTRACE_ON_SEGFAULT},
   };
   return result;
diff --git a/tests/python/disco/test_nvshmem.py 
b/tests/python/disco/test_nvshmem.py
new file mode 100644
index 0000000000..0b16fe9361
--- /dev/null
+++ b/tests/python/disco/test_nvshmem.py
@@ -0,0 +1,114 @@
+# 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.
+"""Basic tests for a Disco nvshmem support"""
+# pylint: disable=missing-docstring
+import tempfile
+
+import numpy as np
+import pytest
+import subprocess
+import threading
+import sys
+
+import tvm
+import tvm.testing
+from tvm.runtime import ShapeTuple
+from tvm.runtime import disco as di
+from tvm.exec import disco_worker as _  # pylint: disable=unused-import
+
+_SOCKET_SESSION_TESTER = None
+
+
+def get_free_port():
+    import socket
+
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind(("", 0))
+    port = s.getsockname()[1]
+    s.close()
+    return port
+
+
+class SocketSessionTester:
+    def __init__(self, num_workers):
+        num_nodes = 2
+        num_groups = 1
+        assert num_workers % num_nodes == 0
+        num_workers_per_node = num_workers // num_nodes
+        server_host = "localhost"
+        server_port = get_free_port()
+        self.sess = None
+
+        def start_server():
+            self.sess = di.SocketSession(
+                num_nodes, num_workers_per_node, num_groups, server_host, 
server_port
+            )
+
+        thread = threading.Thread(target=start_server)
+        thread.start()
+
+        cmd = "tvm.exec.disco_remote_socket_session"
+        self.remote_nodes = []
+        for _ in range(num_nodes - 1):
+            self.remote_nodes.append(
+                subprocess.Popen(
+                    [
+                        "python3",
+                        "-m",
+                        cmd,
+                        server_host,
+                        str(server_port),
+                        str(num_workers_per_node),
+                    ],
+                    stdout=sys.stdout,
+                    stderr=sys.stderr,
+                )
+            )
+
+        thread.join()
+
+    def __del__(self):
+        for node in self.remote_nodes:
+            node.kill()
+        if self.sess is not None:
+            self.sess.shutdown()
+            del self.sess
+
+
+def create_socket_session(num_workers):
+    global _SOCKET_SESSION_TESTER
+    if _SOCKET_SESSION_TESTER is not None:
+        del _SOCKET_SESSION_TESTER
+    _SOCKET_SESSION_TESTER = SocketSessionTester(num_workers)
+    assert _SOCKET_SESSION_TESTER.sess is not None
+    return _SOCKET_SESSION_TESTER.sess
+
+
[email protected]("num_workers", [2, 4])
+def test_nvshmem_init(num_workers):
+    if tvm.get_global_func("runtime.disco.nvshmem.init_nvshmem_uid", True) is 
None:
+        return
+    sess = create_socket_session(num_workers=num_workers)
+    f_init_nvshmem_uid = 
tvm.get_global_func("runtime.disco.nvshmem.init_nvshmem_uid")
+    uid = f_init_nvshmem_uid()
+    init_dfunc = sess.get_global_func("runtime.disco.nvshmem.init_nvshmem")
+    init_dfunc(uid, num_workers)
+    sess.sync_worker_0()
+
+
+if __name__ == "__main__":
+    tvm.testing.main()

Reply via email to