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

junrushao 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 1994f402e6 Enable ccache to accelerate contrib compilation (#16176)
1994f402e6 is described below

commit 1994f402e69281a53df37a77acf935798eb856bb
Author: Yaxing Cai <[email protected]>
AuthorDate: Thu Nov 30 14:25:22 2023 -0800

    Enable ccache to accelerate contrib compilation (#16176)
    
    This PR adds the interface for ccache in `contrib.cc` to enable ccache when 
creating libs or exectuables.
---
 python/tvm/contrib/cc.py            | 58 +++++++++++++++++++++------
 tests/python/contrib/test_ccache.py | 79 +++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 11 deletions(-)

diff --git a/python/tvm/contrib/cc.py b/python/tvm/contrib/cc.py
index ad6a82c49c..918e3c8f72 100644
--- a/python/tvm/contrib/cc.py
+++ b/python/tvm/contrib/cc.py
@@ -33,6 +33,10 @@ def _is_linux_like():
     )
 
 
+def _is_windows_like():
+    return sys.platform == "win32"
+
+
 def get_cc():
     """Return the path to the default C/C++ compiler.
 
@@ -58,7 +62,7 @@ def get_cc():
     return None
 
 
-def create_shared(output, objects, options=None, cc=None):
+def create_shared(output, objects, options=None, cc=None, cwd=None, 
ccache_env=None):
     """Create shared library.
 
     Parameters
@@ -74,13 +78,19 @@ def create_shared(output, objects, options=None, cc=None):
 
     cc : Optional[str]
         The compiler command.
+
+    cwd : Optional[str]
+        The urrent working directory.
+
+    ccache_env : Optional[Dict[str, str]]
+        The environment variable for ccache. Set `None` to disable ccache by 
default.
     """
     cc = cc or get_cc()
 
     if _is_linux_like():
-        _linux_compile(output, objects, options, cc, compile_shared=True)
-    elif sys.platform == "win32":
-        _windows_compile(output, objects, options)
+        _linux_compile(output, objects, options, cc, cwd, ccache_env, 
compile_shared=True)
+    elif _is_windows_like():
+        _windows_compile(output, objects, options, cwd, ccache_env)
     else:
         raise ValueError("Unsupported platform")
 
@@ -133,7 +143,7 @@ def create_staticlib(output, inputs, ar=None):
         raise ValueError("Unsupported platform")
 
 
-def create_executable(output, objects, options=None, cc=None):
+def create_executable(output, objects, options=None, cc=None, cwd=None, 
ccache_env=None):
     """Create executable binary.
 
     Parameters
@@ -149,13 +159,19 @@ def create_executable(output, objects, options=None, 
cc=None):
 
     cc : Optional[str]
         The compiler command.
+
+    cwd : Optional[str]
+        The urrent working directory.
+
+    ccache_env : Optional[Dict[str, str]]
+        The environment variable for ccache. Set `None` to disable ccache by 
default.
     """
     cc = cc or get_cc()
 
     if _is_linux_like():
-        _linux_compile(output, objects, options, cc)
+        _linux_compile(output, objects, options, cc, cwd, ccache_env)
     elif sys.platform == "win32":
-        _windows_compile(output, objects, options)
+        _windows_compile(output, objects, options, cwd, ccache_env)
     else:
         raise ValueError("Unsupported platform")
 
@@ -269,7 +285,9 @@ def cross_compiler(
     return _fcompile
 
 
-def _linux_compile(output, objects, options, compile_cmd, 
compile_shared=False):
+def _linux_compile(
+    output, objects, options, compile_cmd, cwd=None, ccache_env=None, 
compile_shared=False
+):
     cmd = [compile_cmd]
     if compile_cmd != "nvcc":
         if compile_shared or output.endswith(".so") or 
output.endswith(".dylib"):
@@ -288,7 +306,15 @@ def _linux_compile(output, objects, options, compile_cmd, 
compile_shared=False):
         cmd += objects
     if options:
         cmd += options
-    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
stderr=subprocess.STDOUT)
+    env = None
+    if ccache_env is not None:
+        if shutil.which("ccache"):
+            cmd.insert(0, "ccache")
+            env = os.environ.copy()
+            env.update(ccache_env)
+        else:
+            raise ValueError("ccache not found")
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
stderr=subprocess.STDOUT, cwd=cwd, env=env)
     (out, _) = proc.communicate()
     if proc.returncode != 0:
         msg = "Compilation error:\n"
@@ -297,7 +323,7 @@ def _linux_compile(output, objects, options, compile_cmd, 
compile_shared=False):
         raise RuntimeError(msg)
 
 
-def _windows_compile(output, objects, options):
+def _windows_compile(output, objects, options, cwd=None, ccache_env=None):
     cmd = ["clang"]
     cmd += ["-O2"]
 
@@ -312,9 +338,19 @@ def _windows_compile(output, objects, options):
     cmd += objects
     if options:
         cmd += options
+    env = None
+    if ccache_env is not None:
+        if shutil.which("ccache"):
+            cmd.insert(0, "ccache")
+            env = os.environ.copy()
+            env.update(ccache_env)
+        else:
+            raise ValueError("ccache not found")
 
     try:
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
stderr=subprocess.STDOUT)
+        proc = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd, 
env=env
+        )
         (out, _) = proc.communicate()
     except FileNotFoundError:
         raise RuntimeError(
diff --git a/tests/python/contrib/test_ccache.py 
b/tests/python/contrib/test_ccache.py
new file mode 100644
index 0000000000..c46e72c648
--- /dev/null
+++ b/tests/python/contrib/test_ccache.py
@@ -0,0 +1,79 @@
+# 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.
+"""Test contrib.cc with ccache"""
+import os
+import pytest
+import shutil
+import tempfile
+import tvm
+from tvm.contrib.cc import create_shared, create_executable, _is_linux_like, 
_is_windows_like
+
+
+def _src_gen(text):
+    return """
+#include <iostream>
+
+int main() {
+    std::cout << "text";
+    return 0;
+}""".replace(
+        "text", text
+    )
+
+
+def _compile(f_create, text, output):
+    with tempfile.TemporaryDirectory() as temp_dir:
+        src_path = os.path.join(temp_dir, "src.cpp")
+        with open(src_path, "w", encoding="utf-8") as file:
+            file.write(_src_gen(text))
+        log_path = os.path.join(temp_dir, "log.txt")
+        ccache_env = {
+            "CCACHE_COMPILERCHECK": "content",
+            "CCACHE_LOGFILE": log_path,
+        }
+        f_create(output, ["src.cpp"], ["-c"], cwd=temp_dir, 
ccache_env=ccache_env)
+        with open(log_path, "r", encoding="utf-8") as file:
+            log = file.read()
+        return log
+
+
[email protected](shutil.which("ccache") is None, reason="ccache not 
installed")
+def test_shared():
+    if _is_linux_like():
+        _ = _compile(create_shared, "shared", "main.o")
+        log = _compile(create_shared, "shared", "main.o")
+        assert "Succeeded getting cached result" in log
+    elif _is_windows_like():
+        _ = _compile(create_shared, "shared", "main.obj")
+        log = _compile(create_shared, "shared", "main.obj")
+        assert "Succeeded getting cached result" in log
+
+
[email protected](shutil.which("ccache") is None, reason="ccache not 
installed")
+def test_executable():
+    if _is_linux_like():
+        _ = _compile(create_executable, "executable", "main")
+        log = _compile(create_executable, "executable", "main")
+        assert "Succeeded getting cached result" in log
+    elif _is_windows_like():
+        _ = _compile(create_executable, "executable", "main.exe")
+        log = _compile(create_executable, "executable", "main.exe")
+        assert "Succeeded getting cached result" in log
+
+
+if __name__ == "__main__":
+    tvm.testing.main()

Reply via email to