From 36e99d0fc6c1ae6e79584aebffb95610380691f5 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Fri, 17 Jan 2020 13:56:28 +0800 Subject: [PATCH] Add SGD Kernel Go Package (#1637) * add sgd kernel using eigen * test style * move kernel to go dir * add kernel test * refine test * fix eigen3 header file * trigger go unittest * fix ci * fix ci * fix ci * follow comments * trigger go test * update * follow comments * update * update * fix code style * fix dockerfile --- .clang-format | 18 ++++++++++++++++ .gitignore | 2 ++ .pre-commit-config.yaml | 19 +++++++++++++++-- .travis.yml | 1 + elasticdl/Makefile | 11 +++++++++- elasticdl/docker/Dockerfile | 4 ++-- elasticdl/docker/Dockerfile.dev | 4 +++- elasticdl/pkg/kernel/capi/kernel_api.cc | 13 ++++++++++++ elasticdl/pkg/kernel/capi/kernel_api.h | 13 ++++++++++++ elasticdl/pkg/kernel/kernel.go | 13 ++++++++++++ elasticdl/pkg/kernel/kernel_test.go | 27 ++++++++++++++++++++++++ scripts/codestyle/clang_format.hook | 1 + scripts/codestyle/cpplint_precommit.hook | 27 ++++++++++++++++++++++++ 13 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 .clang-format create mode 100644 elasticdl/pkg/kernel/capi/kernel_api.cc create mode 100644 elasticdl/pkg/kernel/capi/kernel_api.h create mode 100644 elasticdl/pkg/kernel/kernel.go create mode 100644 elasticdl/pkg/kernel/kernel_test.go create mode 100644 scripts/codestyle/clang_format.hook create mode 100644 scripts/codestyle/cpplint_precommit.hook diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..b0fa55c40 --- /dev/null +++ b/.clang-format @@ -0,0 +1,18 @@ +--- +Language: Cpp +BasedOnStyle: Google +IndentWidth: 2 +TabWidth: 2 +ContinuationIndentWidth: 4 +AccessModifierOffset: -1 # The private/protected/public has no indent in class +Standard: Cpp11 +AllowAllParametersOfDeclarationOnNextLine: true +BinPackParameters: false +BinPackArguments: false +--- +Language: Proto +BasedOnStyle: Google +IndentWidth: 2 +TabWidth: 2 +ContinuationIndentWidth: 4 +--- diff --git a/.gitignore b/.gitignore index a6939eae2..1ceba3613 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ __pycache__/ # C extensions *.so +*.o +*.a # Distribution / packaging .Python diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cfffcaf9a..2704317f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,11 +21,26 @@ repos: hooks: - id: mypy args: [--ignore-missing-imports, --follow-imports=skip] +- repo: local + hooks: + - id: clang-format + name: clang-format + description: Format files with ClangFormat. + entry: bash ./scripts/codestyle/clang_format.hook -i + language: system + files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto)$ +- repo: local + hooks: + - id: cpplint-cpp-source + name: cpplint + description: Check C++ code style using cpplint.py. + entry: bash ./scripts/codestyle/cpplint_precommit.hook + language: system + files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$ - repo: git://github.com/dnephin/pre-commit-golang rev: v0.3.3 hooks: - id: go-fmt - id: go-lint - id: validate-toml - - id: no-go-testing - + - id: no-go-testing \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 6675798bb..94ae279aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ addons: - docker-ce - python3-pip - python3-setuptools + - clang-format install: - docker version diff --git a/elasticdl/Makefile b/elasticdl/Makefile index 02252af49..981b1a0fc 100644 --- a/elasticdl/Makefile +++ b/elasticdl/Makefile @@ -5,7 +5,10 @@ EDL_PROTO_FILE = elasticdl/proto/elasticdl.proto GO_FILE_DIR = elasticdl/pkg/proto GO_PB_FILE = $(GO_FILE_DIR)/elasticdl.pb.go -all: python_pb $(GO_PB_FILE) +CXX = g++ +CAPI_DIR = elasticdl/pkg/kernel/capi + +all: python_pb $(GO_PB_FILE) kernel python_pb: $(PYTHON) -m grpc_tools.protoc -I . $(EDL_PROTO_FILE) --python_out=. --grpc_python_out=. @@ -13,3 +16,9 @@ python_pb: $(GO_PB_FILE): $(EDL_PROTO_FILE) mkdir -p $(GO_FILE_DIR) protoc -I . $< --go_out=plugins=grpc:. + +kernel: + ${CXX} -O3 -std=c++11 ${CAPI_DIR}/kernel_api.cc -c -o ${CAPI_DIR}/libkernel_api.o + ar crv ${CAPI_DIR}/libkernel_api.a ${CAPI_DIR}/libkernel_api.o +clean: + rm -rf ${CAPI_DIR}/*.o ${CAPI_DIR}/*.a \ No newline at end of file diff --git a/elasticdl/docker/Dockerfile b/elasticdl/docker/Dockerfile index 4587cc10c..f2580135a 100644 --- a/elasticdl/docker/Dockerfile +++ b/elasticdl/docker/Dockerfile @@ -6,7 +6,7 @@ FROM ${BASE_IMAGE} COPY elasticdl/docker/bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN apt-get update && apt-get install -y unzip curl git software-properties-common +RUN apt-get update && apt-get install -y unzip curl git software-properties-common libeigen3-dev COPY elasticdl/requirements.txt /requirements.txt ARG EXTRA_PYPI_INDEX=https://pypi.org/simple @@ -15,4 +15,4 @@ RUN pip install -r /requirements.txt --extra-index-url=${EXTRA_PYPI_INDEX} WORKDIR / ENV PYTHONPATH=/ COPY elasticdl /elasticdl -RUN make -f elasticdl/Makefile +RUN make -f elasticdl/Makefile python_pb diff --git a/elasticdl/docker/Dockerfile.dev b/elasticdl/docker/Dockerfile.dev index 0a30a12aa..a4323704c 100644 --- a/elasticdl/docker/Dockerfile.dev +++ b/elasticdl/docker/Dockerfile.dev @@ -6,7 +6,9 @@ FROM ${BASE_IMAGE} COPY elasticdl/docker/bashrc /etc/bash.bashrc RUN chmod a+rwx /etc/bash.bashrc -RUN apt-get update && apt-get install -y unzip curl git software-properties-common wget +RUN apt update && \ + apt install -y unzip curl git software-properties-common g++ wget \ + libeigen3-dev COPY elasticdl/requirements.txt /requirements.txt COPY elasticdl/requirements-dev.txt /requirements-dev.txt diff --git a/elasticdl/pkg/kernel/capi/kernel_api.cc b/elasticdl/pkg/kernel/capi/kernel_api.cc new file mode 100644 index 000000000..9cf3ca045 --- /dev/null +++ b/elasticdl/pkg/kernel/capi/kernel_api.cc @@ -0,0 +1,13 @@ +#include "kernel_api.h" + +#include + +void SGD(float* grad, float* param, double lr, long long size) { + Eigen::Map> eg{ + grad, static_cast(size)}; + + Eigen::Map> ep{ + param, static_cast(size)}; + + ep -= lr * eg; +} diff --git a/elasticdl/pkg/kernel/capi/kernel_api.h b/elasticdl/pkg/kernel/capi/kernel_api.h new file mode 100644 index 000000000..41afa7de8 --- /dev/null +++ b/elasticdl/pkg/kernel/capi/kernel_api.h @@ -0,0 +1,13 @@ +#ifndef ELASTICDL_PKG_KERNEL_CAPI_KERNEL_API_H_ +#define ELASTICDL_PKG_KERNEL_CAPI_KERNEL_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void SGD(float* grad, float* param, double lr, long long size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/elasticdl/pkg/kernel/kernel.go b/elasticdl/pkg/kernel/kernel.go new file mode 100644 index 000000000..d7c63e41b --- /dev/null +++ b/elasticdl/pkg/kernel/kernel.go @@ -0,0 +1,13 @@ +package kernel + +// #cgo LDFLAGS: -L./capi -lkernel_api +// #include "capi/kernel_api.h" +import "C" +import "unsafe" + +// SGD kernel +func SGD(grad []float32, param []float32, lr float64, size int64) { + gradPtr := (*C.float)(unsafe.Pointer(&grad[0])) + paramPtr := (*C.float)(unsafe.Pointer(¶m[0])) + C.SGD(gradPtr, paramPtr, C.double(lr), C.longlong(size)) +} diff --git a/elasticdl/pkg/kernel/kernel_test.go b/elasticdl/pkg/kernel/kernel_test.go new file mode 100644 index 000000000..3a7805978 --- /dev/null +++ b/elasticdl/pkg/kernel/kernel_test.go @@ -0,0 +1,27 @@ +package kernel + +import "testing" +import "math/rand" +import "github.com/stretchr/testify/assert" + +func TestSGD(t *testing.T) { + const size int = 10 + a := make([]float32, size) + b := make([]float32, size) + var lr float32 = 0.1 + + for i := 0; i < size; i++ { + a[i] = rand.Float32() + b[i] = rand.Float32() + } + + expected := make([]float32, size) + + for i := 0; i < size; i++ { + expected[i] = b[i] - lr*a[i] + } + + SGD(a, b, float64(lr), int64(size)) + + assert.Equal(t, b, expected) +} diff --git a/scripts/codestyle/clang_format.hook b/scripts/codestyle/clang_format.hook new file mode 100644 index 000000000..c46d455a9 --- /dev/null +++ b/scripts/codestyle/clang_format.hook @@ -0,0 +1 @@ +clang-format $@ \ No newline at end of file diff --git a/scripts/codestyle/cpplint_precommit.hook b/scripts/codestyle/cpplint_precommit.hook new file mode 100644 index 000000000..36a9b1604 --- /dev/null +++ b/scripts/codestyle/cpplint_precommit.hook @@ -0,0 +1,27 @@ +#!/bin/bash + +TOTAL_ERRORS=0 +if [[ ! $TRAVIS_BRANCH ]]; then + # install cpplint on local machine. + if [[ ! $(which cpplint) ]]; then + pip install cpplint + fi + # diff files on local machine. + files=$(git diff --cached --name-status | awk 'Extra open brace or missing close brace2}') +else + # diff files between PR and latest commit on Travis CI. + branch_ref=$(git rev-parse "$TRAVIS_BRANCH") + head_ref=$(git rev-parse HEAD) + files=$(git diff --name-status $branch_ref $head_ref | awk 'Extra open brace or missing close brace2}') +fi +# The trick to remove deleted files: https://stackoverflow.com/a/2413151 +for file in $files; do + if [[ $file =~ ^(patches/.*) ]]; then + continue; + else + cpplint --filter=-readability/fn_size $file; + TOTAL_ERRORS=$(expr $TOTAL_ERRORS + $?); + fi +done + +exit $TOTAL_ERRORS \ No newline at end of file