Commit 0206d3e1 authored by hezhengjun's avatar hezhengjun

Initial commit

parents
# Taken from: http://stackoverflow.com/questions/5711120/gitignore-without-binary-files
# Ignore all
*
# Unignore all with extensions
!*.*
# Unignore all dirs
!*/
# Unignore Makefiles
!Makefile
### Above combination will ignore all files without extension ###
# Ignore C/C++ intermediate files
*.o
*.a
*.so
*.dylib
*.out
# Ignore Maven build folder
target
# Ignore npm modules folder
node_modules
# Ignore Vim saved sessions
Session.vim
# Go interlanguage call examples
Examples of calls between Go and C/C++ and calling Go from dynamic languages.
The examples are designed to work on Mac and Linux. If you're using Windows,
feel free to reach out to me to request updates for examples that don't work
correctly.
## Calls from Go to C (`go_to_c` folder)
These examples use [cgo](https://golang.org/cmd/cgo/) to enable calls to C.
- Calling a C snippet in the cgo comment: [c_in_comment](https://github.com/draffensperger/go-interlang/tree/master/go_to_c/c_in_comment/main.go)
- Calling a C statically-linked library (`.a` file): [static_c_lib](https://github.com/draffensperger/go-interlang/tree/master/go_to_c/static_c_lib)
- Calling a C dynamically-linked library (`.so` or `.dylib`): [dynamic_c_lib](https://github.com/draffensperger/go-interlang/tree/master/go_to_c/dynamic_c_lib)
## Calls from Go to C++ (`go_to_cxx` folder)
C++ has more complex calling conventions (e.g. function overloading, inheritance, templates) and so it uses [name mangling](https://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B) which adds a step when calling it from Go. Below are ways to do it.
- Calling C++ via a C wrapper function: [c_wrapper](https://github.com/draffensperger/go-interlang/tree/master/go_to_cxx/c_wrapper)
- Calling C++ via an auto-generated SWIG wrapper:
[swig](https://github.com/draffensperger/go-interlang/tree/master/go_to_cxx/swig)
## Calls from C/C++ to Go (`c_to_go` folder)
- Calling from Go to C and back again: [to_c_and_back](https://github.com/draffensperger/go-interlang/tree/master/go_from_c/to_c_and_back)
- Passing a Go func as function pointer callback to C: [callbacks](https://github.com/draffensperger/go-interlang/tree/master/go_from_c/callbacks)
- Calling a Go static library with `buildmode=c-archive` from C: [static_go_lib](https://github.com/draffensperger/go-interlang/tree/master/c_to_go/static_go_lib)
- Calling a Go dynamic lib from C++ [cxx_to_go_dyn_lib](https://github.com/draffensperger/go-interlang/tree/master/c_to_go/cxx_to_go_dyn_lib)
- Calling Go from C using [gccgo](https://golang.org/doc/install/gccgo)
## Calls from Python/Node.js/Ruby/Java to Go (`dyn_langs_to_go` folder)
Go now allows building a C-compatible dynamically-linked library with `buildmode=c-shared`. That allows any language that can call C dynamic libraries to call Go.
- Call from Python via [ctypes](https://docs.python.org/2/library/ctypes.html):
[python_to_go.py](https://github.com/draffensperger/go-interlang/blob/master/dyn_langs_to_go/python_to_go.py)
- Call from Node.js via [ffi](https://github.com/node-ffi/node-ffi) npm module:
[nodejs_to_go.js](https://github.com/draffensperger/go-interlang/blob/master/dyn_langs_to_go/nodejs_to_go.js)
- Call from Ruby via [ffi](https://github.com/ffi/ffi) gem: [ruby_to_go.rb](https://github.com/draffensperger/go-interlang/blob/master/dyn_langs_to_go/ruby_to_go.rb)
- Call from Java via [JNA](https://github.com/java-native-access/jna): [JavaToGo.java](https://github.com/draffensperger/go-interlang/blob/master/dyn_langs_to_go/java_to_go/src/main/java/javatogo/JavaToGo.java)
# Cross-language call benchmarks
There is a cost to calling between languages a Go to C call is about 50x slower
than a pure Go call and a Ruby FFI call is about 30x slower than a pure Ruby
call. For more details, see the [benchmarks](https://github.com/draffensperger/go-interlang/tree/master/benchmarks) section.
# Helpful Links
Cgo documentation: [golang.og/cmd/cgo/](https://golang.org/cmd/cgo/)
Documentation for `go` command (see especially `go build` section and "Calling between Go and C"):
[golang.org/cmd/go/](https://golang.org/cmd/go/)
SWIG Documentation: [swig.org](http://swig.org/)
SWIG Go examples: [github.com/swig/swig/tree/master/Examples/go](https://github.com/swig/swig/tree/master/Examples/go)
Gccgo documentation: [golang.org/doc/install/gccgo](https://golang.org/doc/install/gccgo)
# License
The code in this repo is [MIT Licensed](http://opensource.org/licenses/MIT).
TARGETS=go_only c_only go_to_c_adds go_to_c_sum go_concurrent go_to_c_concurrent go_to_go_http go_to_c_no_op
.PHONY: clean $(TARGETS)
all: $(TARGETS)
go_only:
make -C $@
c_only:
make -C $@
go_to_c_adds:
make -C $@
go_to_c_sum:
make -C $@
go_concurrent:
make -C $@
go_to_c_concurrent:
make -C $@
go_to_go_http:
make -C $@
go_to_c_no_op:
make -C $@
clean:
rm -f go_only/main c_only/main go_to_c_adds/main go_to_c_sum/main \
go_only/libharmonic.* go_concurrent/main go_to_c_concurrent/main \
go_to_go_http/main go_to_c_no_op/libgo_no_op.*
# Go inter-language call benchmarks
These benchmarks explore the cost of calls from Go to C and Ruby to Go, as well
as HTTP calls and Go to C call concurrency implications.
To run all the benchmarks, run `./make_and_run.sh`, which compiles the code and
calls the shell scripts for the various benchmarks.
For a more general comparison of language performance, see
[The Computer Language Benchmarks Game](http://benchmarksgame.alioth.debian.org/)
(source copied on GitHub at
[github.com/nbraud/benchmarksgame](https://github.com/nbraud/benchmarksgame)).
The raw results from runs of the benchmarks on my machine in
[results.md](https://github.com/draffensperger/go-interlang/blob/master/benchmarks/results.md).
Two helpful links on the performance and concurrency implications of Go to C
calls are a golang-nuts thread
[What is the overhead of calling a C function from Go?](https://groups.google.com/forum/#!topic/golang-nuts/RTtMsgZi88Q) and a Stack Overflow question
[C code and goroutine scheduling](http://stackoverflow.com/questions/28354141/c-code-and-goroutine-scheduling).
## Go to C call cost
The most direct way to measure the cost of a Go to C call is via a no-op
benchmark of a cgo call and a pure Go call. That benchmark can be found in the
`go_to_c_no_op` folder. The benchmark depends on disabling function inlining in
the Go compiler via the `-gcflags "-N -l"` flag.
The no-op benchmark on my machine found the cost of a Go to C call to be about
160 ns, and the cost of a Go call to be about 3ns, making a cgo call about 50x
slower than a Go call.
That overhead broadly concurs with the
[golang-nuts thread on cgo overhead](https://groups.google.com/forum/#!topic/golang-nuts/RTtMsgZi88Q)
in that the difference is noticable due to the need for a stack change, though some
people there seemed to have a ratio closer to 9-25x. Someone there did have the
same value of 160ns for a cgo call. The thread is from 2013 so the Go runtime
may have changed since their benchmarks were run.
A less direct measure is comparing how a Go to C calculation of the harmonic
series (with a cgo call for each item of the series) compares with the pure Go
and pure C versions. The Go to C harmonic seris with n-times cgo calls took
about 40x longer than the pure C version, which broadly fits with the ratio of
50x for the no-op benchmark since its time is dominated by the cost of the cgo
calls.
Basically calling C from Go incurs a cost but you can still do millions /
second (1s / 160ns = 6.25 million), so it makes sense to use cgo calls, just not
in a tight, high performance loop.
## Ruby to Go call cost
The Ruby FFI cost from the no-op Ruby to Go vs. pure Ruby function call
benchmark got a ratio of about 28x.
The Ruby to Go harmonic series calculation with n-times FFI calls took 24x as
long as the pure Ruby version, which fits well with the no-op benchmark assuming
that Go code running in those FFI calls gave a slight speedup compared to pure
Ruby + pure FFI cost.
The Go calculation for the harmonic series (0.55s)was about 10x faster than the
pure Ruby one (5.5s). Ruby using a single FFI call (0.66s) was 9x faster than
the pure Ruby version.
So calling Go from Ruby can be helpful, but again, avoid repeated calls from
Ruby in a tight loop.
## Concurrency implications
As mentioned in the
## HTTP call cost
c_only:
gcc -O3 -o main *.c
#include <stdio.h>
#include <stdlib.h>
double add_harmonic(double total_so_far, long i) {
return total_so_far + 1.0 / i;
}
double harmonic_sum(long n) {
double sum = 0.0;
for (long i = 1; i <= n; i++) {
sum = add_harmonic(sum, i);
}
return sum;
}
int main(int argc, char *argv[]) {
long n = atol(argv[1]);
printf("\n\nC only:\nSum for n=1..%ld of 1/n =\n", n);
printf("%.15f", harmonic_sum(n));
return 0;
}
package main
import (
"fmt"
"os"
"runtime"
"strconv"
"sync"
)
func addHarmonic(totalSoFar float64, i int) float64 {
return totalSoFar + 1.0/float64(i)
}
func harmonicRange(start, end int) float64 {
sum := 0.0
for i := start; i <= end; i++ {
sum = addHarmonic(sum, i)
}
return sum
}
var partialSums []float64
var wg sync.WaitGroup
func calcPartialSum(goroutine, start, end int) {
defer wg.Done()
sum := harmonicRange(start, end)
partialSums[goroutine] = sum
// fmt.Printf("goroutine %v for n=%v..%v got %v\n", goroutine, start, end, sum)
}
func main() {
n, _ := strconv.Atoi(os.Args[1])
goroutines, err := strconv.Atoi(os.Args[2])
if err != nil {
goroutines = runtime.NumCPU()
}
fmt.Printf("\n\nGo with %v goroutines and GOMAXPROCS=%v:\n", goroutines,
os.Getenv("GOMAXPROCS"))
fmt.Printf("Sum for n=1..%v of 1/n =\n", n)
wg.Add(goroutines)
partialSums = make([]float64, goroutines)
rangeLen := n / goroutines
for i := 0; i < goroutines; i++ {
start := i*rangeLen + 1
end := start + rangeLen - 1
go calcPartialSum(i, start, end)
}
wg.Wait()
sum := 0.0
for i := 0; i < goroutines; i++ {
sum += partialSums[i]
}
fmt.Printf("%v", sum)
}
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
else
DYLIB_EXT := .so
endif
all:
go build -o main
go build -o libharmonic$(DYLIB_EXT) -buildmode=c-shared
package main
import "C"
import (
"fmt"
"os"
"strconv"
)
//export AddHarmonic
func AddHarmonic(totalSoFar C.double, i C.long) C.double {
return totalSoFar + 1.0/C.double(i)
}
//export HarmonicSum
func HarmonicSum(n C.long) C.double {
sum := C.double(0.0)
for i := C.long(1); i <= n; i++ {
sum = AddHarmonic(sum, i)
}
return sum
}
func main() {
n_int, _ := strconv.Atoi(os.Args[1])
n := C.long(n_int)
fmt.Printf("\n\nGo only:\nSum for n=1..%v of 1/n =\n", n)
fmt.Printf("%v", HarmonicSum(n))
}
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/draffensperger/go-interlang/benchmarks/go_only */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef __complex float GoComplex64;
typedef __complex double GoComplex128;
// static assertion to make sure the file is being used on architecture
// at least with matching size of GoInt.
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern double AddHarmonic(double p0, long int p1);
extern double HarmonicSum(long int p0);
#ifdef __cplusplus
}
#endif
package main
import (
"fmt"
"net/http"
"os"
"strconv"
)
func harmonic(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
iStr := path[1:len(path)]
if iStr == "stop" {
os.Exit(0)
}
i, err := strconv.Atoi(iStr)
if err == nil {
fmt.Fprintf(w, "%v", 1.0/float64(i))
}
}
func main() {
http.HandleFunc("/", harmonic)
http.ListenAndServe(":8000", nil)
}
#include <stdio.h>
#include <stdlib.h>
double add_harmonic(double total_so_far, long i) {
return total_so_far + 1.0 / i;
}
#ifndef ADD_HARMONIC_H
#define ADD_HARMONIC_H
double add_harmonic(double total_so_far, long i);
#endif
package main
// #include "add_harmonic.h"
import "C"
import (
"fmt"
"os"
"strconv"
)
func HarmonicSum(n C.long) C.double {
sum := C.double(0.0)
for i := C.long(1); i <= n; i++ {
sum = C.add_harmonic(sum, i)
}
return sum
}
func main() {
n_int, _ := strconv.Atoi(os.Args[1])
n := C.long(n_int)
fmt.Println("\n\nGo with n times C add_harmonic calls:")
fmt.Printf("Sum for n=1..%v of 1/n =\n", n)
fmt.Printf("%v", HarmonicSum(n))
}
#include <stdio.h>
#include <stdlib.h>
double add_harmonic(double total_so_far, long i) {
return total_so_far + 1.0 / i;
}
double harmonic_range(long start, long end) {
double sum = 0.0;
for (long i = start; i <= end; i++) {
sum = add_harmonic(sum, i);
}
return sum;
}
#ifndef HARMONIC_RANGE_H
#define HARMONIC_RANGE_H
double harmonic_range(long start, long end);
#endif
package main
// #include "harmonic_range.h"
import "C"
import (
"fmt"
"os"
"runtime"
"strconv"
"sync"
)
var partialSums []C.double
var wg sync.WaitGroup
func calcPartialSum(goroutine, start, end int) {
defer wg.Done()
partialSums[goroutine] = C.harmonic_range(C.long(start), C.long(end))
}
func main() {
n, _ := strconv.Atoi(os.Args[1])
goroutines, err := strconv.Atoi(os.Args[2])
if err != nil {
goroutines = runtime.NumCPU()
}
fmt.Printf("\n\nGo wth %v goroutines calling C and GOMAXPROCS=%v:\n", goroutines,
os.Getenv("GOMAXPROCS"))
fmt.Printf("Sum for n=1..%v of 1/n =\n", n)
wg.Add(goroutines)
partialSums = make([]C.double, goroutines)
rangeLen := n / goroutines
for i := 0; i < goroutines; i++ {
start := i*rangeLen + 1
end := start + rangeLen - 1
go calcPartialSum(i, start, end)
}
wg.Wait()
sum := C.double(0.0)
for i := 0; i < goroutines; i++ {
sum += partialSums[i]
}
fmt.Printf("%v", sum)
}
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
else
DYLIB_EXT := .so
endif
libgo_no_op:
go build -o libgo_no_op$(DYLIB_EXT) -buildmode=c-shared
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/draffensperger/go-interlang/benchmarks/go_to_c_no_op */
/* Start of preamble from import "C" comments. */
#line 3 "/Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_no_op/main.go"
#include "no_op.h"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef __complex float GoComplex64;
typedef __complex double GoComplex128;
// static assertion to make sure the file is being used on architecture
// at least with matching size of GoInt.
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern void GoOnlyNoOp();
#ifdef __cplusplus
}
#endif
package main
// #include "no_op.h"
import "C"
func GoToCNoOp() {
C.no_op()
}
//export GoOnlyNoOp
func GoOnlyNoOp() {
}
func main() {
}
package main
import "testing"
func BenchmarkGoToCNoOp(b *testing.B) {
for i := 0; i < b.N; i++ {
GoToCNoOp()
}
}
func BenchmarkGoOnlyNoOp(b *testing.B) {
for i := 0; i < b.N; i++ {
GoOnlyNoOp()
}
}
#include "no_op.h"
void no_op() {
}
#ifndef NO_OP_H
#define NO_OP_H
void no_op();
#endif
# The -gcflags "-N -l" is necessary to prevent the Go compiler from inlining to
# Go no-op function. See:
# https://code.google.com/p/go/issues/detail?id=3363
# https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/6m8bZYik8Ss
go test -gcflags "-N -l" -bench=.
# Results on my machine:
#
# testing: warning: no tests to run
# PASS
# BenchmarkGoToCNoOp-8 10000000 155 ns/op
# BenchmarkGoOnlyNoOp-8 500000000 3.15 ns/op
# ok github.com/draffensperger/go-interlang/benchmarks/go_to_c_no_op 3.614s
#include <stdio.h>
#include <stdlib.h>
double add_harmonic(double total_so_far, int i) {
return total_so_far + 1.0 / i;
}
double harmonic_sum(int n) {
double sum = 0.0;
for (int i = 1; i <= n; i++) {
sum = add_harmonic(sum, i);
}
return sum;
}
#ifndef HARMONIC_SUM_H
#define HARMONIC_SUM_H
double harmonic_sum(int n);
#endif
package main
// #include "harmonic_sum.h"
import "C"
import (
"fmt"
"os"
"strconv"
)
func main() {
n_int, _ := strconv.Atoi(os.Args[1])
n := C.int(n_int)
fmt.Println("\n\nGo with 1 time C harmonic_sum call:")
fmt.Printf("Sum for n=1..%v of 1/n =\n", n)
fmt.Printf("%v", C.harmonic_sum(n))
}
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
)
func GetHarmonic(n int) float64 {
resp, err := http.Get("http://localhost:8000/" + strconv.Itoa(n))
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var harmonicBytes []byte
harmonicBytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
var harmonic float64
harmonic, err = strconv.ParseFloat(string(harmonicBytes[:]), 64)
if err != nil {
log.Fatal(err)
}
return harmonic
}
func HarmonicSum(n int) float64 {
sum := 0.0
for i := 1; i <= n; i++ {
sum += GetHarmonic(i)
}
return sum
}
func main() {
n, _ := strconv.Atoi(os.Args[1])
fmt.Println("\n\nGo with n times HTTP calls:")
fmt.Printf("Sum for n=1..%v of 1/n =\n", n)
fmt.Printf("%v", HarmonicSum(n))
}
make clean
make
runners/concurrency.sh
runners/go_to_c.sh
runners/ruby_to_go.sh
runners/http.sh
# Benchmark results
Results run on a 2015 MacBook Pro with Intel with 2.8 GHz Intel Core i7 (Quad
Core with hyperthreading, Turbo Boost up to 4.0 Ghz). Running OS X 10.11 El
Capitan. C compiler is Apple LLVM version 7.0.0 (clang-700.0.72). Used Go 1.5
and Ruby 2.2.3.
## Harmonic series benchmarks
### Go to C call benchmarks
Output of `./go_to_c.sh`:
```
=======================================
--- Go to C interlang call cost ---
=======================================
C only:
Sum for n=1..800000000 of 1/n =
21.077337951159535
real 0m2.928s
user 0m2.917s
sys 0m0.003s
Go only:
Sum for n=1..800000000 of 1/n =
21.077337951159535
real 0m5.699s
user 0m5.665s
sys 0m0.014s
Go with 1 time C harmonic_sum call:
Sum for n=1..800000000 of 1/n =
21.077337951159535
real 0m2.987s
user 0m2.937s
sys 0m0.008s
Go with n times C add_harmonic calls:
Sum for n=1..800000000 of 1/n =
21.077337951159535
real 1m55.440s
user 1m55.221s
sys 0m0.247s
Benchmarking Go to C no-op calls vs. Go no-op calls
testing: warning: no tests to run
PASS
BenchmarkGoToCNoOp-8 10000000 154 ns/op
BenchmarkGoOnlyNoOp-8 500000000 3.18 ns/op
ok github.com/draffensperger/go-interlang/benchmarks/go_to_c_no_op 3.639s
```
### Go to Ruby call benchmarks
Output of `./ruby_to_go.sh`:
```
=======================================
--- Ruby to Go FFI cost ---
=======================================
C only:
Sum for n=1..80000000 of 1/n =
18.774752863787992
real 0m0.304s
user 0m0.300s
sys 0m0.002s
Go only:
Sum for n=1..80000000 of 1/n =
18.77475286378799
real 0m0.553s
user 0m0.547s
sys 0m0.005s
Ruby only:
Sum for n=1..80000000 of 1/n =
18.77475286378799
real 0m5.453s
user 0m5.421s
sys 0m0.027s
Ruby with 1 time Go HarmonicSum call:
Sum for n=1..80000000 of 1/n =
18.77475286378799
real 0m0.662s
user 0m0.623s
sys 0m0.035s
Ruby with n times Go AddHarmonic calls:
Sum for n=1..80000000 of 1/n =
18.77475286378799
real 2m0.839s
user 1m12.624s
sys 0m48.176s
Ruby to Go no-op FFI call benchmark:
user system total real
48.200000 30.660000 78.860000 ( 78.977828)
Ruby function call benchmark:
user system total real
2.830000 0.010000 2.840000 ( 2.839761)
```
### Concurrency benchmarks
Output of `./concurrency.sh`:
```
=======================================
--- Go to C and concurrency ---
=======================================
Go only:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m56.482s
user 0m56.253s
sys 0m0.122s
C only:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m29.354s
user 0m29.284s
sys 0m0.037s
Go with 1 goroutines and GOMAXPROCS=1:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m55.320s
user 0m55.204s
sys 0m0.103s
Go wth 1 goroutines calling C and GOMAXPROCS=1:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m29.568s
user 0m29.463s
sys 0m0.055s
Go with 8 goroutines and GOMAXPROCS=1:
Sum for n=1..8000000000 of 1/n =
23.379923043590928
real 0m55.868s
user 0m55.725s
sys 0m0.134s
Go wth 8 goroutines calling C and GOMAXPROCS=1:
Sum for n=1..8000000000 of 1/n =
23.379923043590928
real 0m7.479s
user 0m57.470s
sys 0m0.092s
Go with 8 goroutines and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.379923043590928
real 0m7.858s
user 1m0.744s
sys 0m0.091s
Go wth 8 goroutines calling C and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.379923043590928
real 0m7.610s
user 0m58.936s
sys 0m0.098s
Go with 4 goroutines and GOMAXPROCS=4:
Sum for n=1..8000000000 of 1/n =
23.379923043595255
real 0m14.466s
user 0m57.521s
sys 0m0.116s
Go wth 4 goroutines calling C and GOMAXPROCS=4:
Sum for n=1..8000000000 of 1/n =
23.379923043595255
real 0m8.057s
user 0m31.979s
sys 0m0.066s
Go with 1 goroutines and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m56.975s
user 0m56.851s
sys 0m0.115s
Go wth 1 goroutines calling C and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.379923043614358
real 0m29.049s
user 0m29.005s
sys 0m0.028s
Go with 80000 goroutines and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.37992304358969
real 0m7.633s
user 0m59.654s
sys 0m0.107s
Go wth 80000 goroutines calling C and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.37992304358969
real 0m7.492s
user 0m58.981s
sys 0m0.199s
Go with 800000 goroutines and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
23.37992304358886
real 0m9.613s
user 1m5.301s
sys 0m0.897s
Go wth 800000 goroutines calling C and GOMAXPROCS=8:
Sum for n=1..8000000000 of 1/n =
runtime/cgo: pthread_create failed: Resource temporarily unavailable
SIGABRT: abort
PC=0x7fff8791d0ae m=2
goroutine 0 [idle]:
goroutine 1 [runnable]:
sync.(*WaitGroup).Wait(0x4192fd0)
/usr/local/go/src/sync/waitgroup.go:99
main.main()
/Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:39 +0x41c
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1696 +0x1
goroutine 138214 [runnable, locked to thread]:
main._Cfunc_harmonic_range(0x5252d441, 0x5252fb50, 0x3ede5e2cc45e113a)
github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/_obj/_cgo_gotypes.go:41 +0x3a
main.calcPartialSum(0x21b84, 0x5252d441, 0x5252fb50)
/Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:18 +0x58
created by main.main
/Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:37 +0x3f5
... many more goroutines printing status, aborted output ...
```
### HTTP call benchmarks
```
=======================================
--- Ruby to Go localhost HTTP cost ---
=======================================
C only:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m0.003s
user 0m0.001s
sys 0m0.001s
Go only:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m0.005s
user 0m0.001s
sys 0m0.003s
Ruby only:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m0.070s
user 0m0.042s
sys 0m0.025s
Ruby with n times Go AddHarmonic calls:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m0.155s
user 0m0.106s
sys 0m0.045s
Go with n times C add_harmonic calls:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m0.007s
user 0m0.003s
sys 0m0.003s
Go with n times HTTP calls:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m1.181s
user 0m0.522s
sys 0m0.398s
Ruby with n times HTTP calls:
Sum for n=1..16000 of 1/n =
10.257590915797937
real 0m9.424s
user 0m2.586s
sys 0m1.660s
```
n = ARGV[0].to_i
puts "\n\nRuby only:"
puts "Sum for n=1..#{n} of 1/n ="
def harmonic(j)
1.0 / j
end
sum = 0.0
i = 1
while i <= n do
sum += harmonic(i)
i += 1
end
print sum
require 'ffi'
module GoHarmonic
extend FFI::Library
ffi_lib "go_only/libharmonic.#{FFI::Platform::LIBSUFFIX}"
attach_function :AddHarmonic, [:double, :int], :double
end
n = ARGV[0].to_i
puts "\n\nRuby with n times Go AddHarmonic calls:"
puts "Sum for n=1..#{n} of 1/n ="
sum = 0.0
i = 1
while i <= n do
sum = GoHarmonic.AddHarmonic(sum, i)
i += 1
end
print sum
require 'net/http'
n = ARGV[0].to_i
puts "\n\nRuby with n times HTTP calls:"
puts "Sum for n=1..#{n} of 1/n ="
sum = 0.0
i = 1
http = Net::HTTP.new('localhost', 8000)
while i <= n do
harmonic = http.request(Net::HTTP::Get.new('/' + i.to_s)).body.to_f
sum += harmonic
i += 1
end
print sum
require 'ffi'
require 'benchmark'
module GoNoOp
extend FFI::Library
ffi_lib "go_to_c_no_op/libgo_no_op.#{FFI::Platform::LIBSUFFIX}"
attach_function :GoOnlyNoOp, [], :void
end
n = 50000000
puts "\n\nBenchmarking #{n} Ruby to Go no-op FFI calls"
Benchmark.bm do |b|
b.report { n.times { GoNoOp.GoOnlyNoOp } }
end
def no_op
end
puts "\n\nBenchmarking #{n} Ruby to only no-op function calls"
Benchmark.bm do |b|
b.report { n.times { no_op } }
end
require 'ffi'
module GoHarmonic
extend FFI::Library
ffi_lib "go_only/libharmonic.#{FFI::Platform::LIBSUFFIX}"
attach_function :HarmonicSum, [:int], :double
end
n = ARGV[0].to_i
puts "\n\nRuby with 1 time Go HarmonicSum call:"
puts "Sum for n=1..#{n} of 1/n ="
print GoHarmonic.HarmonicSum(n)
N=8000000000
echo
echo =======================================
echo --- Go to C and concurrency ---
echo =======================================
time go_only/main $N
time c_only/main $N
time GOMAXPROCS=1 go_concurrent/main $N 1
time GOMAXPROCS=1 go_to_c_concurrent/main $N 1
time GOMAXPROCS=1 go_concurrent/main $N 8
time GOMAXPROCS=1 go_to_c_concurrent/main $N 8
time GOMAXPROCS=8 go_concurrent/main $N 8
time GOMAXPROCS=8 go_to_c_concurrent/main $N 8
time GOMAXPROCS=4 go_concurrent/main $N 4
time GOMAXPROCS=4 go_to_c_concurrent/main $N 4
time GOMAXPROCS=8 go_concurrent/main $N 1
time GOMAXPROCS=8 go_to_c_concurrent/main $N 1
time GOMAXPROCS=8 go_concurrent/main $N 80000
time GOMAXPROCS=8 go_to_c_concurrent/main $N 80000
time GOMAXPROCS=8 go_concurrent/main $N 800000
# GOMAXPROCS=8 go_to_c_concurrent/main 8000000000 800000 &> out.txt
# Start of out.txt :
# Go wth 800000 goroutines calling C and GOMAXPROCS=8:
# Sum for n=1..8000000000 of 1/n =
# runtime/cgo: pthread_create failed: Resource temporarily unavailable
# SIGABRT: abort
# PC=0x7fff8791d0ae m=2
# goroutine 0 [idle]:
# goroutine 1 [runnable]:
# sync.(*WaitGroup).Wait(0x4192fd0)
# /usr/local/go/src/sync/waitgroup.go:99
# main.main()
# /Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:39 +0x41c
# goroutine 17 [syscall, locked to thread]:
# runtime.goexit()
# /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1
# goroutine 138214 [runnable, locked to thread]:
# main._Cfunc_harmonic_range(0x5252d441, 0x5252fb50, 0x3ede5e2cc45e113a)
# github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/_obj/_cgo_gotypes.go:41 +0x3a
# main.calcPartialSum(0x21b84, 0x5252d441, 0x5252fb50)
# /Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:18 +0x58
# created by main.main
# /Users/dave/Dropbox/Programming/golang/src/github.com/draffensperger/go-interlang/benchmarks/go_to_c_concurrent/main.go:37 +0x3f5
N=800000000
echo
echo =======================================
echo --- Go to C interlang call cost ---
echo =======================================
time c_only/main $N
time go_only/main $N
time go_to_c_sum/main $N
time go_to_c_adds/main $N
echo
echo
echo Benchmarking Go to C no-op calls vs. Go no-op calls
(cd go_to_c_no_op && ./run_benchmark.sh)
N=16000
echo
echo =======================================
echo --- Ruby to Go localhost HTTP cost ---
echo =======================================
time c_only/main $N
time go_only/main $N
time ruby ruby/ruby_only.rb $N
time ruby ruby/ruby_to_go_adds.rb $N
time go_to_c_adds/main $N
go run go_server/main.go &
sleep 1
time go_to_go_http/main $N
curl -s localhost:8000/stop
go run go_server/main.go &
sleep 1
time ruby ruby/ruby_to_go_http.rb $N
curl -s localhost:8000/stop
N=80000000
echo
echo =======================================
echo --- Ruby to Go FFI cost ---
echo =======================================
time c_only/main $N
time go_only/main $N
time ruby ruby/ruby_only.rb $N
time ruby ruby/ruby_to_go_sum.rb $N
time ruby ruby/ruby_to_go_adds.rb $N
# The Ruby code will do the time benchmarking via Ruby benchmark lib
ruby ruby/ruby_to_go_no_op.rb
# Calling from Go to C and back
To run this example run `go build` then `./to_c_and_back`.
Note: When using //export, the "C" preamble can only have, declarations
(no definitions), because it gets copied twice. (See
[cgo docs](https://golang.org/cmd/cgo/)).
#include "adder.h"
#include <stdio.h>
void add(int x, int y, void (*result_callback)(int)) {
printf("C says: adding %i and %i\n", x, y);
int total = x + y;
// Call function pointer
result_callback(total);
}
#ifndef ADDER_H
#define ADDER_H
void add(int x, int y, void (*result_callback)(int));
#endif
#include "callback.h"
#include <stdio.h>
// _cgo_export.h is auto-generated and has Go //export funcs
#include "_cgo_export.h"
void c_to_go_callback(int total) {
printf("C callback got total %i\n", total);
GoTotalCallback(total);
}
void add_with_go_callback(int x, int y) {
printf("C passing Go function pointer..\n");
// Within C you can pass an exported Go func as a pointer
add(x, y, GoTotalCallback);
}
#ifndef CALLBACK_H
#define CALLBACK_H
// This typedef is used by Go
typedef void (*callback_fn) ();
void c_to_go_callback(int total);
void add_with_go_callback(int x, int y);
#endif
package main
import "fmt"
// #include "adder.h"
// #include "callback.h"
import "C"
var total int
// The //export allows C to call the Go func
//export GoTotalCallback
func GoTotalCallback(callbackTotal C.int) {
fmt.Printf("Go callback got total %d\n", callbackTotal)
total = int(callbackTotal)
}
func main() {
fmt.Printf("Go says: calling C callback add..\n")
// With cgo you can't call C function pointers directly,
// but you can pass then to C functions that can call them
C.add(40, 2, C.callback_fn(C.c_to_go_callback))
fmt.Printf("Go says: 1st result is %d\n\n", total)
fmt.Printf("Go says: calling add with Go callback..\n")
C.add_with_go_callback(100, 1)
fmt.Printf("Go says: 2nd result is %d\n", total)
}
.PHONY: clean
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
else
DYLIB_EXT := .so
endif
all: go_adder/libadder$(DYLIB_EXT) main
go_adder/libadder$(DYLIB_EXT):
$(MAKE) -C go_adder
main:
g++ -o $@ main.cxx -I./go_adder -L./go_adder -ladder
clean:
rm -f main go_adder/libadder.*
# Calling a Go dynamically linked library from C++
To make an run this example, run `./make_and_run.sh`.
The key tricks here are setting the include and link parameters for `g++`, using
`-buildmode=c-shared` for `go build` (see `Makefile`) and then setting the
library path environment variable for the compiled program to find the library
(see `make_and_run.sh`).
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
else
DYLIB_EXT := .so
endif
libadder$(DYLIB_EXT):
# New in Go 1.5, build Go dynamic lib
go build -o $@ -buildmode=c-shared
clean:
rm -f libadder.*
package main
import "fmt"
import "C"
//export Add
func Add(x, y int) int {
fmt.Printf("Go says: adding %v and %v\n", x, y)
return x + y
}
func main() {} // Required but ignored
/* Created by "go tool cgo" - DO NOT EDIT. */
/* package github.com/draffensperger/go-interlang/c_to_go/cxx_to_go_dyn_lib/go_adder */
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef __complex float GoComplex64;
typedef __complex double GoComplex128;
// static assertion to make sure the file is being used on architecture
// at least with matching size of GoInt.
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef struct { char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern GoInt Add(GoInt p0, GoInt p1);
#ifdef __cplusplus
}
#endif
#include "libadder.h"
#include <iostream>
using namespace std;
int main() {
cout << "C++ says: calling Go dynamic lib.." << endl;
long total = Add(30, 12);
cout << "C++ says: got total as " << total << endl;
}
make clean
make
# Add a blank line between make and run
echo
# Give the search path for the library
# LD_LIBRARY_PATH is for Linux, DYLD_LIBRARY_PATH for Mac
LD_LIBRARY_PATH=./go_adder DYLD_LIBRARY_PATH=./go_adder ./main
all: main
main: main.o adder.o
gccgo *.o -o $@
main.o: main.c
gcc -c $< -o $@
adder.o: adder.go
gccgo -c $< -o $@ -fgo-prefix=example
clean:
rm -f main *.o
# Example of using gccgo for Go and C interoperability
This is an example of a C program calling a Go function that gets statically
compiled into the program. To see the example install gccgo (`sudo apt-get install gccgo` on Ubuntu), then run `make` and `./main`.
This example was adapted from a
[stack overflow answer](http://stackoverflow.com/questions/6125683/call-go-functions-from-c)
.
## Helpful links
Gccgo documentation: [golang.org/doc/install/gccgo](https://golang.org/doc/install/gccgo)
A writeup on using gccgo to link C++ and Go: [Statically Link C++ Code With Go Code](https://cxwangyi.wordpress.com/2011/03/28/statically-linking-c-code-with-go-code/)
Note that as of this writing (October 2015), gccgo doesn't build on Mac OS X.
See
[github.com/golang/go/issues/463](https://github.com/golang/go/issues/463) and
[github.com/golang/go/issues/10727](https://github.com/golang/go/issues/10727).
package main
func Add(a, b int) int {
return a + b
}
#include <stdio.h>
extern int go_add(int, int) __asm__ ("example.main.Add");
int main() {
printf("C says: about to call Go...\n");
int x = go_add(2, 3);
printf("C says: got result as: %d\n", x);
}
.PHONY: clean
CCFLAGS := -pthread
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Darwin)
# This prevents a warning about PIE on Mac OS
CCFLAGS += -Wl,-no_pie
endif
static_go_lib: adder/libadder.a
gcc $(CCFLAGS) -o $@ *.c $<
adder/libadder.a:
$(MAKE) -C adder
clean:
rm -f adder/libadder.a static_go_lib
https://github.com/shazow/gohttplib
http://stackoverflow.com/questions/30896892/using-go-1-5-buildmode-c-archive-with-net-http-server-linked-from-c
.PHONY: clean
libadder.a:
go build -buildmode=c-archive -o $@
clean:
rm -f libadder.*
package main
import "fmt"
import "C"
//export GoAdder
func GoAdder(x, y int) int {
fmt.Printf("Go says: adding %v and %v\n", x, y)
return x + y
}
func main() {} // Required but ignored
#include <stdio.h>
#include "adder/libadder.h"
int main() {
printf("C says: about to call Go...\n");
int total = GoAdder(1, 7);
printf("C says: Go calculated our total as %i\n", total);
return 0;
}
# Calling from Go to C and back
To run this example run `go build` then `./to_c_and_back`.
The example shows how to use the `//export` cgo comment to call Go functions
from C. Cgo will generate a `_cgo_export.h` with definitions for the exported Go
functions, which can then be included in a C file in the same folder as the Go
project.
Note: When using //export, the "C" preamble can only have, declarations
(no definitions), because it gets copied twice. (See
[cgo docs](https://golang.org/cmd/cgo/)).
#include "adder.h"
#include <stdio.h>
// _cgo_export.h is auto-generated
// and has Go //export funcs
#include "_cgo_export.h"
void add_and_give_go_total(int x, int y) {
printf("C: adding %i + %i, giving Go total\n",
x, y);
GiveGoTotal(x + y);
}
#ifndef ADDER_H
#define ADDER_H
void add_and_give_go_total(int x, int y);
#endif
package main
import "fmt"
// #include "adder.h"
import "C"
// The //export allows C to call the Go func
//export GiveGoTotal
func GiveGoTotal(total C.int) {
fmt.Printf("Go: got total from C %d\n", total)
}
func main() {
fmt.Printf("Go: calling C to add numbers..\n")
C.add_and_give_go_total(40, 2)
}
.PHONY: clean
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
else
DYLIB_EXT := .so
endif
go_adder/libadder$(DYLIB_EXT):
$(MAKE) -C go_adder
clean:
$(MAKE) -C go_adder clean
# Calling Go from interpreted languages
This folder contains code for calling a Go shared library from Python, Ruby,
Javascript (Node.js) and Java.
## Installing dependencies
- For Python, it probably already has [ctypes](https://docs.python.org/2/library/ctypes.html) installed
- For Ruby, `gem install ffi`
- For Javascript, `npm install ffi`
- For Java, install [Maven](https://maven.apache.org/),
then `mvn -f java_to_go/pom.xml install` (or `cd java_to_go` then `mvn install` and `cd ..`).
That will install [JNA](https://github.com/java-native-access/jna) and compile
the Java source.
To install all the dependencies use `./install_ffi_deps./sh`
## Running the examples
To run the examples, first run `make` to build the Go shared library, then run
`./run_examples.sh` to run all of the examples.
## Other similar examples
http://stackoverflow.com/questions/15879993/writing-a-ruby-extension-in-go-golang
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
DYLIB_EXT := .dylib
FLAGS := -ldflags -s
else
DYLIB_EXT := .so
FLAGS := ''
endif
libadder$(DYLIB_EXT):
# New in Go 1.5, build Go dynamic lib
go build $(FLAGS) -o $@ -buildmode=c-shared
clean:
rm -f libadder.*
package main
import "fmt"
import "C"
//export Add
func Add(x, y int) int {
fmt.Printf("Go says: adding %v and %v\n", x, y)
return x + y
}
func main() {} // Required but ignored
gem install ffi
npm install ffi
mvn -f java_to_go/pom.xml install
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<jna.version>RELEASE</jna.version>
</properties>
<groupId>com.github.draffensperger</groupId>
<artifactId>java_to_go</artifactId>
<name>java_to_go</name>
<version>0.1</version>
<description>Java example of calling a shared library in Go</description>
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>${jna.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>net.java.dev.jna</groupId>-->
<!-- <artifactId>jna</artifactId>-->
<!-- <version>${jna.version}</version>-->
<!-- <classifier>platform</classifier>-->
<!-- </dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package javatogo;
import java.io.File;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class JavaToGo {
static GoAdder GO_ADDER;
static {
String os = System.getProperty("os.name").toLowerCase();
String libExtension;
if (os.contains("mac os")) {
libExtension = "dylib";
} else if (os.contains("windows")) {
libExtension = "dll";
} else {
libExtension = "so";
}
String pwd = System.getProperty("user.dir");
String lib = pwd + "/go_adder/libadder." + libExtension;
GO_ADDER = (GoAdder) Native.loadLibrary(lib, GoAdder.class);
}
public interface GoAdder extends Library {
long Add(long x, long y);
}
public static void main(String[] args) {
System.out.println("Java says: about to call Go ..");
long total = GO_ADDER.Add(30, 12);
System.out.println("Java says: result is " + total);
}
}
var ffi = require('ffi');
var lib = ffi.Library('go_adder/libadder', {
'Add': [ 'long', [ 'long', 'long' ] ]
});
console.log('JavaScript says: about to call Go ..');
var total = lib.Add(39, 3);
console.log('JavaScript says: result is ' + total);
from sys import platform as _platform
from ctypes import cdll
def lib_extension():
if _platform == 'darwin':
return 'dylib'
elif _platform == 'win32':
return 'dll'
else:
return 'so'
lib = cdll.LoadLibrary('go_adder/libadder.' + lib_extension())
print "Python say: about to call Go ..."
total = lib.Add(40, 2)
print "Python says: result is " + str(total)
require 'ffi'
module GoAdder
extend FFI::Library
ext = FFI::Platform::LIBSUFFIX
ffi_lib "./go_adder/libadder.#{ext}"
attach_function :Add, [:long, :long], :long
end
puts "Ruby says: about to call Go ..."
total = GoAdder.Add(41, 1)
puts "Ruby says: result is #{total}"
echo 'Calling Go library from interpreted languages ...'
echo
echo '---- Ruby ----'
ruby ruby_to_go.rb
echo
echo '---- Python ----'
python python_to_go.py
echo
echo '---- Node.js ----'
# Use the global path, so we can do "npm install ffi -g" and avoid creating a
# local node_modules folder.
node nodejs_to_go.js
echo
echo '---- Java ----'
java -cp java_to_go/target/classes:java_to_go/target/dependency/* javatogo.JavaToGo
package main
import (
"fmt"
"unsafe"
)
/*
// This preamble is interpreted as C code
// See cgo docs: https://golang.org/cmd/cgo/
#include <stdio.h>
#include <stdlib.h>
int add(int x, int y) {
printf("C says: adding %i + %i\n", x, y);
return x + y;
}
*/
import "C"
func main() {
total := C.add(10, 20)
fmt.Printf("Go says: result is %v\n", total)
gostr := "Go can call C's puts from stdio"
// C.CString uses C heap, we must free it
var cstr *C.char = C.CString(gostr)
// defer is convenient for clean-up
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
}
# Calling a dynamically-linked C library from Go
To run this example, you first need install
[LPSolve](http://lpsolve.sourceforge.net/5.5/). See below for instructions.
Then just run `go build` to build the Go executable, and run it with
`./dynamic_c_lib`.
## How to install LPSolve
Here's how you could download and extract the LPSolve library for 64-bit Linux:
```
LP_URL=http://sourceforge.net/projects/lpsolve/files/lpsolve/5.5.2.0/lp_solve_5.5.2.0_dev_ux64.tar.gz
mkdir lpsolve
curl -L $LP_URL | tar xvz -C lpsolve
```
To install LPSolve on Mac OS X, install [MacPorts](https://www.macports.org/),
then run `sudo port install lp_solve`.
## More complete LPSolve Go wrapper
The `lp.go` file is a simplified version of the same file in the
[golp](https://github.com/draffensperger/golp)
package I made to wrap the
[LPSolve](http://lpsolve.sourceforge.net/5.5/)
Mixed Integer Linear Programming (MILP) solver.
For more discussion of concepts in this example, see my blog post
[Calling a Linear Solver C Library From Go](http://davidraff.com/calling-a-linear-programming-solver-from-go).
## Other links and notes
[golang-nuts: Passing a slice/array pointer between Go/C](https://groups.google.com/forum/#!topic/golang-nuts/DXCIP6pMMN0)
Something I learned by forgetting a \n in printf:
[Go doesn't flush C streams on program exit](https://github.com/golang/go/issues/8808),
and
[printf doesn't flush unless there is a \n in the format](http://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-string),
so if you try a printf without \n in your C code called by Go the message won't
show up.
package main
/*
// "linux" and "darwin" below are build constraints
// See: https://golang.org/pkg/go/build/
// On Mac, assume LPSolve is installed via MacPorts
#cgo darwin CFLAGS: -I/opt/local/include/lpsolve
#cgo darwin LDFLAGS: -L/opt/local/lib -llpsolve55
// On Linux, assume that lpsolve is local directory
#cgo linux CFLAGS: -I./lpsolve
#cgo linux LDFLAGS: -L./lpsolve -llpsolve55 -Wl,-rpath=./lpsolve
#include <lp_lib.h>
*/
import "C"
import (
"fmt"
"runtime"
)
type LP struct {
ptr *C.lprec
}
func NewLP(rows, cols int) *LP {
l := new(LP)
l.ptr = C.make_lp(C.int(rows), C.int(cols))
// Free the lp structure in the Go finalizer
runtime.SetFinalizer(l, deleteLP)
return l
}
func deleteLP(l *LP) {
fmt.Println("Go finalizer says: deleting C linear program")
C.delete_lp(l.ptr)
}
type ConstraintType int
const (
_ = iota // don't use 0
LE // LE == 1
GE // GE == 2
EQ // EQ == 3
)
func (l *LP) AddConstraint(row []float64,
ct ConstraintType, rightHand float64) {
// Pass a slice as a C array do &slice[0]
C.add_constraint(l.ptr, (*C.double)(&row[0]),
C.int(ct), C.double(rightHand))
}
func (l *LP) SetObjFn(row []float64) {
C.set_obj_fn(l.ptr, (*C.double)(&row[0]))
}
func (l *LP) SetMaximize() {
C.set_maxim(l.ptr)
}
func (l *LP) Solve() {
C.solve(l.ptr)
}
func (l *LP) Objective() float64 {
return float64(C.get_objective(l.ptr))
}
func (l *LP) Variables() []float64 {
numCols := int(C.get_Ncolumns(l.ptr))
row := make([]float64, numCols)
C.get_variables(l.ptr, (*C.double)(&row[0]))
return row
}
package main
import (
"fmt"
"runtime"
)
func solveLinearProgram() {
lp := NewLP(0, 2)
lp.AddConstraint([]float64{0.0, 110.0, 30.0}, LE, 4000.0)
lp.AddConstraint([]float64{0.0, 1.0, 1.0}, LE, 75.0)
lp.SetObjFn([]float64{0.0, 143.0, 60.0})
lp.SetMaximize()
fmt.Println("Go says: About to solve lineary program ...")
lp.Solve()
vars := lp.Variables()
fmt.Println("\nGo says results are: ")
fmt.Printf("Plant %.3f acres of barley\n", vars[0])
fmt.Printf("And %.3f acres of wheat\n", vars[1])
fmt.Printf("For optimal profit of $%.2f\n", lp.Objective())
}
func main() {
solveLinearProgram()
// Force GC to illustrate finalizer running
runtime.GC()
runtime.GC()
runtime.GC()
}
.PHONY: clean all
all: c_static_lib go_executable
c_static_lib:
gcc -c greetings/*.c
ar rs greetings.a *.o
rm -rf *.o
go_executable:
go build -o static_c_lib
clean:
rm -rf *.o *.a static_c_lib
# Calling a C static library from Go
To build the C static library and Go executable, run `make`. Then run
`./go_to_c_static` to run the Go program.
## Another similar example
[github.com/shadowmint/go-static-linking](https://github.com/shadowmint/go-static-linking)
#include "greetings.h"
#include <stdio.h>
void greet_in_english(char* name) {
printf("C says: Hey %s, how's it going?\n", name);
}
#include "greetings.h"
#include <stdio.h>
void greet_in_german(char* name) {
printf("C sagt: Hallo %s, wie geht's?\n", name);
}
#ifndef GREETINGS_H
#define GREETINGS_H
void greet_in_german(char* name);
void greet_in_english(char* name);
#endif
package main
import "fmt"
import "unsafe"
// #cgo CFLAGS: -I${SRCDIR}/greetings
// #cgo LDFLAGS: ${SRCDIR}/greetings.a
// #include <stdlib.h>
// #include <greetings.h>
import "C"
func main() {
fmt.Printf("Go says: static C library greetings coming..\n")
john := C.CString("John")
defer C.free(unsafe.Pointer(john))
johannes := C.CString("Johannes")
defer C.free(unsafe.Pointer(johannes))
C.greet_in_english(john)
C.greet_in_german(johannes)
fmt.Printf("Go says bye!\n")
}
# Call C++ from Go with cgo via a C wrapper
To run the example, run `go build` then `./c_wrapper`.
## Exploring C++ name manging with nm utility
The [nm](http://linux.die.net/man/1/nm) utility explores symbol names in object
files.
To see what the C++ name mangling looks like, run `g++ -c point.cxx` then
`nm point.o | grep distance`. You will see how C++ exports the `distance_to`
function to include some extra decoration to show e.g. how it's connected to the
`Point` class.
To see the cleaner `extern "C"` name, run `g++ -c *.cxx`, and then `nm
wrap_point.o | grep distance`. You'll see an external reference to the C++
mangled name for `Point::distance_to`, but you'll also see the cleaner
`_distance_between` name which Go can call.
## Other similar examples
Here are a couple other similar examples of calling C++ from Go without SWIG
using a C wrapper:
[github.com/wangkuiyi/go-cpp/](https://github.com/wangkuiyi/go-cpp/)
[github.com/burke/howto-go-with-cpp](https://github.com/burke/howto-go-with-cpp)
package main
// #include "wrap_point.hxx"
import "C"
import "fmt"
func main() {
fmt.Println("Hi from Go, about to calculate distance in C++ ...")
distance := C.distance_between(1.0, 1.0, 2.0, 2.0)
fmt.Printf("Go has result, distance is: %v\n", distance)
}
#include "point.hxx"
#include <math.h>
#include <iostream>
Point::Point(double x, double y): x_(x), y_(y) {
}
double Point::distance_to(Point p) {
std::cout << "C++ says: finding distance between (" << x_ << "," << y_
<< "), and (" << p.x_ << "," << p.y_ << ")" << std::endl;
double x_dist = x_ - p.x_;
double y_dist = y_ - p.y_;
return sqrt(x_dist * x_dist + y_dist * y_dist);
}
#ifndef POINT_H
#define POINT_H
class Point {
public:
Point(double x, double y);
double distance_to(Point p);
private:
double x_, y_;
};
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment