├── .dockerignore ├── lib.cc ├── BUILD.bazel ├── .gitignore ├── Dockerfile ├── lib.go ├── Makefile ├── LICENSE ├── main.cc ├── README.md └── WORKSPACE /.dockerignore: -------------------------------------------------------------------------------- 1 | /bazel-* 2 | -------------------------------------------------------------------------------- /lib.cc: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif /* __cplusplus */ 4 | 5 | void ExampleCppFunction() {} 6 | 7 | #ifdef __cplusplus 8 | } 9 | #endif /* __cplusplus */ 10 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary") 2 | load("@rules_cc//cc:defs.bzl", "cc_binary") 3 | 4 | cc_binary( 5 | name = "main", 6 | srcs = ["main.cc"], 7 | deps = [ 8 | ":lib_go.cc", 9 | ], 10 | ) 11 | 12 | go_binary( 13 | name = "lib_go", 14 | srcs = ["lib.go", "lib.cc"], 15 | cgo = True, 16 | linkmode = "c-archive", 17 | ) 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories generated by bazel. 2 | /bazel-* 3 | 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update -qq \ 4 | && apt-get install -qqy \ 5 | g++ unzip zip curl openjdk-11-jdk git \ 6 | && apt-get clean \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | RUN curl -L -o /tmp/bazel-installer.sh \ 10 | https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-installer-linux-x86_64.sh && \ 11 | chmod +x /tmp/bazel-installer.sh && \ 12 | /tmp/bazel-installer.sh 13 | -------------------------------------------------------------------------------- /lib.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | void ExampleCppFunction(); 5 | */ 6 | import "C" 7 | 8 | // RepeatExampleCppFunction calls ExampleCppFunc defined in C n times. 9 | //export RepeatExampleCppFunction 10 | func RepeatExampleCppFunction(n int) { 11 | for i := 0; i < n; i++ { 12 | C.ExampleCppFunction() 13 | } 14 | } 15 | 16 | // ExampleGoFunction is an empty function exposed to C. 17 | //export ExampleGoFunction 18 | func ExampleGoFunction() {} 19 | 20 | func main() {} 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | run: docker 2 | docker run --rm -it --volume=$(shell pwd):/repo -w /repo \ 3 | --volume=/tmp/cgo-peformance-test/.cache:/root/.cache \ 4 | cgo-peformance-test bash -c " \ 5 | bazel run -c opt //:main && \ 6 | cat /proc/cpuinfo" 7 | .PHONY: run 8 | 9 | docker: 10 | docker build -t cgo-peformance-test . 11 | .PHONY: docker 12 | 13 | ################################################################################ 14 | # For debugging. 15 | ################################################################################ 16 | 17 | start: docker 18 | docker run --rm -d --volume=$(shell pwd):/repo -w /repo \ 19 | --volume=/tmp/cgo-peformance-test/.cache:/root/.cache \ 20 | --name=cgo-peformance-test \ 21 | cgo-peformance-test sleep infinity 22 | .PHONY: start 23 | 24 | stop: 25 | docker kill cgo-peformance-test 26 | .PHOY: stop 27 | 28 | exec: 29 | docker exec -it cgo-peformance-test bash 30 | .PHONY: exec 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kentaro IMAJO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "lib_go.h" 8 | 9 | void RepeatExampleGoFunction(int n) { 10 | for (int i = 0; i < n; i++) { 11 | ExampleGoFunction(); 12 | } 13 | } 14 | 15 | void Benchmark(const std::string& name, const std::function& f) { 16 | printf("%s:\n", name.c_str()); 17 | auto measure = [f](int n) { 18 | auto start_time = std::chrono::system_clock::now(); 19 | f(n); 20 | return static_cast( 21 | std::chrono::duration_cast( 22 | std::chrono::system_clock::now() - start_time).count()) * 1e-9; 23 | }; 24 | // Find n that takes 100 ms. 25 | double duration_per_call = measure(1); 26 | for (int i = 0; i < 10; i++) { 27 | int n = int(0.1 / duration_per_call) + 1; 28 | double duration_of_n = measure(n); 29 | duration_per_call = duration_of_n / n; 30 | if (duration_of_n > 0.05) { 31 | break; 32 | } 33 | } 34 | int n = int(1 / duration_per_call) + 1; 35 | double duration_of_n = measure(n); 36 | duration_per_call = duration_of_n / n; 37 | printf(" %.09f seconds / call\n", duration_per_call); 38 | } 39 | 40 | int main() { 41 | Benchmark("C++ to Go", RepeatExampleGoFunction); 42 | Benchmark("Go to C++", RepeatExampleCppFunction); 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CGO Performance Test 2 | TL;DR: Go calls C++ in 73 ns, C++ calls Go in 5.7 us on MacBook Pro (2.6GHz, Turbo Boost 4.30 GHz). 3 | 4 | ``` 5 | cgo-performance-test % make 6 | docker build -t cgo-peformance-test . 7 | Sending build context to Docker daemon 69.63kB 8 | ... 9 | Successfully tagged cgo-peformance-test:latest 10 | docker run --rm -it --volume=/Users/imos/git/cgo-performance-test:/repo -w /repo \ 11 | --volume=/tmp/cgo-peformance-test/.cache:/root/.cache \ 12 | cgo-peformance-test bash -c " \ 13 | bazel run -c opt //:main && \ 14 | cat /proc/cpuinfo" 15 | Starting local Bazel server and connecting to it... 16 | INFO: Analyzed target //:main (35 packages loaded, 6788 targets configured). 17 | INFO: Found 1 target... 18 | INFO: Deleting stale sandbox base /root/.cache/bazel/_bazel_root/6530f9eb448d96e7552a3c3a29b6cd2b/sandbox 19 | Target //:main up-to-date: 20 | bazel-bin/main 21 | INFO: Elapsed time: 100.940s, Critical Path: 71.42s 22 | INFO: 2 processes: 2 processwrapper-sandbox. 23 | INFO: Build completed successfully, 3 total actions 24 | INFO: Build completed successfully, 3 total actions 25 | C++ to Go: 26 | 0.000005798 seconds / call 27 | Go to C++: 28 | 0.000000073 seconds / call 29 | processor : 0 30 | vendor_id : GenuineIntel 31 | cpu family : 6 32 | model : 158 33 | model name : Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz 34 | stepping : 10 35 | cpu MHz : 2600.000 36 | cache size : 9216 KB 37 | physical id : 0 38 | siblings : 1 39 | core id : 0 40 | cpu cores : 1 41 | apicid : 0 42 | initial apicid : 0 43 | fpu : yes 44 | fpu_exception : yes 45 | cpuid level : 22 46 | wp : yes 47 | flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht pbe syscall nx pdpe1gb lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq dtes64 ds_cpl ssse3 sdbg fma cx16 xtpr pcid sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch pti fsgsbase bmi1 hle avx2 bmi2 erms rtm xsaveopt arat 48 | bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs 49 | bogomips : 5184.00 50 | clflush size : 64 51 | cache_alignment : 64 52 | address sizes : 39 bits physical, 48 bits virtual 53 | power management: 54 | ``` 55 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | http_archive( 4 | name = "io_bazel_rules_go", 5 | sha256 = "6a68e269802911fa419abb940c850734086869d7fe9bc8e12aaf60a09641c818", 6 | urls = [ 7 | "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.23.0/rules_go-v0.23.0.tar.gz", 8 | "https://github.com/bazelbuild/rules_go/releases/download/v0.23.0/rules_go-v0.23.0.tar.gz", 9 | ], 10 | ) 11 | 12 | http_archive( 13 | name = "bazel_gazelle", 14 | sha256 = "bfd86b3cbe855d6c16c6fce60d76bd51f5c8dbc9cfcaef7a2bb5c1aafd0710e8", 15 | urls = [ 16 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.0/bazel-gazelle-v0.21.0.tar.gz", 17 | "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.0/bazel-gazelle-v0.21.0.tar.gz", 18 | ], 19 | ) 20 | 21 | load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains") 22 | 23 | go_rules_dependencies() 24 | 25 | go_register_toolchains() 26 | 27 | #### Google Flags 28 | http_archive( 29 | name = "com_github_gflags_gflags", 30 | strip_prefix = "gflags-2.2.2", 31 | urls = [ 32 | "https://github.com/gflags/gflags/archive/v2.2.2.tar.gz", 33 | ], 34 | ) 35 | 36 | bind( 37 | name = "gflags", 38 | actual = "@com_github_gflags_gflags//:gflags", 39 | ) 40 | 41 | #### Google Logging 42 | http_archive( 43 | name = "com_github_google_glog", 44 | strip_prefix = "glog-0.4.0", 45 | urls = [ 46 | "https://github.com/google/glog/archive/v0.4.0.tar.gz", 47 | ], 48 | ) 49 | 50 | bind( 51 | name = "glog", 52 | actual = "@com_github_google_glog//:glog", 53 | ) 54 | 55 | #### Google Test 56 | http_archive( 57 | name = "gtest_repo", 58 | url = "https://github.com/google/googletest/archive/release-1.8.0.zip", 59 | sha256 = "f3ed3b58511efd272eb074a3a6d6fb79d7c2e6a0e374323d1e6bcbcc1ef141bf", 60 | strip_prefix = "googletest-release-1.8.0", 61 | build_file = "//external:gtest.BUILD", 62 | ) 63 | 64 | bind( 65 | name = "gtest", 66 | actual = "@gtest_repo//:gtest", 67 | ) 68 | 69 | bind( 70 | name = "gtest_main", 71 | actual = "@gtest_repo//:gtest_gmock", 72 | ) 73 | 74 | ################################################################################ 75 | # For generated targets. 76 | ################################################################################ 77 | 78 | load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") 79 | 80 | gazelle_dependencies() 81 | 82 | go_repository( 83 | name = "com_github_eknkc_amber", 84 | importpath = "github.com/eknkc/amber", 85 | sum = "h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=", 86 | version = "v0.0.0-20171010120322-cdade1c07385", 87 | ) 88 | 89 | go_repository( 90 | name = "com_github_go_sql_driver_mysql", 91 | importpath = "github.com/go-sql-driver/mysql", 92 | sum = "h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=", 93 | version = "v1.5.0", 94 | ) 95 | 96 | go_repository( 97 | name = "com_github_gorilla_mux", 98 | importpath = "github.com/gorilla/mux", 99 | sum = "h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=", 100 | version = "v1.7.4", 101 | ) 102 | 103 | go_repository( 104 | name = "com_github_gorilla_securecookie", 105 | importpath = "github.com/gorilla/securecookie", 106 | sum = "h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=", 107 | version = "v1.1.1", 108 | ) 109 | 110 | go_repository( 111 | name = "com_github_gorilla_sessions", 112 | importpath = "github.com/gorilla/sessions", 113 | sum = "h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=", 114 | version = "v1.2.0", 115 | ) 116 | 117 | go_repository( 118 | name = "com_github_songmu_strrand", 119 | importpath = "github.com/Songmu/strrand", 120 | sum = "h1:EoNWRkd+8wioWv5fo8RhGwrRSdqlo0NelrFe7gadIL8=", 121 | version = "v0.0.0-20181014100012-5195340ba52c", 122 | ) 123 | 124 | go_repository( 125 | name = "com_github_unrolled_render", 126 | importpath = "github.com/unrolled/render", 127 | sum = "h1:baO+NG1bZSF2WR4zwh+0bMWauWky7DVrTOfvE2w+aFo=", 128 | version = "v1.0.3", 129 | ) 130 | --------------------------------------------------------------------------------