Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into variable_input
commit
fd46fe3817
@ -0,0 +1,107 @@
|
||||
A few months ago when we were trying to replace CMake with Bazel, @emailweixu suggested that we rewrite those handy Bazel functions using CMake. Now it seems that it's the right time to get this done, as we are facing problems from the porting of Majel and the development of new the parameter server using Go and C++.
|
||||
|
||||
Here are some initial thoughts. Your comments are welcome!
|
||||
|
||||
### Required CMake Function
|
||||
|
||||
I think we need only the following few CMake functions to make a project description mean and clean:
|
||||
|
||||
| C++ | CUDA C++ | Go |
|
||||
|---|---|---|
|
||||
| cc_library | nv_library | go_library |
|
||||
| cc_binary | nv_binary | go_binary |
|
||||
| cc_test | nv_test | go_test |
|
||||
|
||||
- The `_library` functions generate .a files from source code.
|
||||
- The `_binary` functions generate executable binary files.
|
||||
- The `_test` functions generate executable unit test files. They work like `_binary` but links `-lgtest` and `-lgtest_main`.
|
||||
|
||||
The difference between `nv_` functions and `cc_` functions is that the former use `nvcc` instead of the system-default C++ compiler.
|
||||
|
||||
Both `nv_` and `cc_` functions enables C++11 (-std=c++11).
|
||||
|
||||
Also,
|
||||
|
||||
- to describe external dependencies, we need `external_library`.
|
||||
- to build shared libraries, we need `shared_library`.
|
||||
|
||||
### An Example Project
|
||||
|
||||
Suppose that we have aforementioned functions defined in our `/cmake` directory. The following example `CMakeLists.txt` describes a project including the following source files:
|
||||
|
||||
- tensor.h
|
||||
- tensor.cc
|
||||
- tensor_test.cc
|
||||
- ops.h
|
||||
- ops.cu
|
||||
- ops_test.cu
|
||||
- api.go
|
||||
- api_test.go
|
||||
|
||||
Suppose that ops.cu depends on CUDNN.
|
||||
|
||||
```cmake
|
||||
# cc_binary parses tensor.cc and figures out that target also depend
|
||||
# on tensor.h.
|
||||
cc_binary(tensor
|
||||
SRCS
|
||||
tensor.cc)
|
||||
|
||||
# The dependency to target tensor implies that if any of
|
||||
# tensor{.h,.cc,_test.cc} is changed, tensor_test need to be re-built.
|
||||
cc_test(tensor_test
|
||||
SRCS
|
||||
tensor_test.cc
|
||||
DEPS
|
||||
tensor)
|
||||
|
||||
# I don't have a clear idea what parameters external_library need to
|
||||
# have. @gangliao as a CMake expert would have better ideas.
|
||||
external_library(cudnn
|
||||
....)
|
||||
|
||||
# Suppose that ops.cu depends on external target CUDNN. Also, ops.cu
|
||||
# include global functions that take Tensor as their parameters, so
|
||||
# ops depend on tensor. This implies that if any of tensor.{h.cc},
|
||||
# ops.{h,cu} is changed, ops need to be re-built.
|
||||
nv_library(ops
|
||||
SRCS
|
||||
ops.cu
|
||||
DEPS
|
||||
tensor
|
||||
cudnn) # cudnn is defined later.
|
||||
|
||||
nv_test(ops_test
|
||||
SRCS
|
||||
ops_test.cu
|
||||
DEPS
|
||||
ops)
|
||||
|
||||
# Because api.go defines a GO wrapper to ops and tensor, it depends on
|
||||
# both. This implies that if any of tensor.{h,cc}, ops.{h,cu}, or
|
||||
# api.go is changed, api need to be re-built.
|
||||
go_library(api
|
||||
SRCS
|
||||
api.go
|
||||
DEPS
|
||||
tensor # Because ops depend on tensor, this line is optional.
|
||||
ops)
|
||||
|
||||
go_test(api_test
|
||||
SRCS
|
||||
api_test.go
|
||||
DEPS
|
||||
api)
|
||||
|
||||
|
||||
# This builds libapi.so. shared_library might use CMake target
|
||||
# api_shared so to distinguish it from above target api.
|
||||
shared_library(api
|
||||
DEPS
|
||||
api)
|
||||
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
As above example CMakeLists.txt executes, each function invocation adds "nodes" to a dependency graph. It also use this graph to generate CMake commands including `add_executable`, `add_dependencies`, `target_link_libraries`, and `add_test`.
|
@ -0,0 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
if(GTEST_INCLUDE_DIR AND GTEST_LIBRARIES)
|
||||
message("-- Found gtest (include: ${GTEST_INCLUDE_DIR}, library: ${GTEST_LIBRARIES})")
|
||||
else()
|
||||
# find #include <majel/xx.h>
|
||||
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
|
||||
include_directories(${PARENT_DIR})
|
||||
|
||||
# find cmake directory modules
|
||||
get_filename_component(PARENT_DIR ${PARENT_DIR} DIRECTORY)
|
||||
get_filename_component(PARENT_DIR ${PARENT_DIR} DIRECTORY)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PARENT_DIR}/cmake")
|
||||
|
||||
# enable c++11
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
# enable gtest
|
||||
set(THIRD_PARTY_PATH ./third_party)
|
||||
set(WITH_TESTING ON)
|
||||
include(external/gtest)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
project(cxx_go CXX C Go)
|
||||
|
||||
include(cmake/golang.cmake)
|
||||
include(cmake/flags.cmake)
|
||||
|
||||
ExternalGoProject_Add(pserver github.com/PaddlePaddle/Paddle/paddle/go/pserver)
|
||||
add_go_library(client STATIC pserver)
|
||||
add_subdirectory(test)
|
@ -0,0 +1,239 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
typedef enum {
|
||||
PADDLE_ELEMENT_TYPE_INT32 = 0,
|
||||
PADDLE_ELEMENT_TYPE_UINT32 = 1,
|
||||
PADDLE_ELEMENT_TYPE_INT64 = 2,
|
||||
PADDLE_ELEMENT_TYPE_UINT64 = 3,
|
||||
PADDLE_ELEMENT_TYPE_FLOAT32 = 4,
|
||||
PADDLE_ELEMENT_TYPE_FLOAT64 = 5,
|
||||
} paddle_element_type;
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
paddle_element_type element_type;
|
||||
unsigned char* content;
|
||||
int content_len;
|
||||
} paddle_parameter, paddle_gradient;
|
||||
|
||||
static inline void paddle_release_param(paddle_parameter* param) {
|
||||
if (param != NULL) {
|
||||
if (param->name != NULL) {
|
||||
free(param->name);
|
||||
}
|
||||
|
||||
if (param->content != NULL) {
|
||||
free(param->content);
|
||||
}
|
||||
|
||||
free(param);
|
||||
}
|
||||
}
|
||||
|
||||
typedef int client;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/PaddlePaddle/Paddle/paddle/go/pserver"
|
||||
)
|
||||
|
||||
var nullPtr = unsafe.Pointer(uintptr(0))
|
||||
var mu sync.Mutex
|
||||
var handleMap = make(map[C.client]*pserver.Client)
|
||||
var curHandle C.client
|
||||
|
||||
func add(c *pserver.Client) C.client {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
client := curHandle
|
||||
curHandle++
|
||||
handleMap[client] = c
|
||||
return client
|
||||
}
|
||||
|
||||
func get(client C.client) *pserver.Client {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return handleMap[client]
|
||||
}
|
||||
|
||||
func remove(client C.client) *pserver.Client {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
h := handleMap[client]
|
||||
delete(handleMap, client)
|
||||
return h
|
||||
}
|
||||
|
||||
func cArrayToSlice(p unsafe.Pointer, len int) []byte {
|
||||
if p == nullPtr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create a Go clice backed by a C array,
|
||||
// reference: https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices
|
||||
return (*[1 << 30]byte)(p)[:len:len]
|
||||
}
|
||||
|
||||
//export paddle_new_pserver_client
|
||||
func paddle_new_pserver_client(addr *C.char) C.client {
|
||||
c := pserver.NewClient(C.GoString(addr))
|
||||
return add(c)
|
||||
}
|
||||
|
||||
//export paddle_pserver_client_release
|
||||
func paddle_pserver_client_release(client C.client) {
|
||||
c := remove(client)
|
||||
c.Cleanup()
|
||||
}
|
||||
|
||||
//export paddle_begin_init_params
|
||||
func paddle_begin_init_params(client C.client, pserver_config unsafe.Pointer, config_len C.int) C.int {
|
||||
c := get(client)
|
||||
b := cArrayToSlice(pserver_config, int(config_len))
|
||||
selected, err := c.BeginInitParams(b)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
if selected {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//export paddle_init_param
|
||||
func paddle_init_param(client C.client, param C.paddle_parameter, param_config unsafe.Pointer, config_len C.int) C.int {
|
||||
et := pserver.ElementType(param.element_type)
|
||||
name := C.GoString(param.name)
|
||||
content := cArrayToSlice(unsafe.Pointer(param.content), int(param.content_len))
|
||||
pc := pserver.ParameterWithConfig{
|
||||
Param: pserver.Parameter{Name: name, ElementType: et, Content: content},
|
||||
Config: cArrayToSlice(param_config, int(config_len)),
|
||||
}
|
||||
c := get(client)
|
||||
err := c.InitParam(pc)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export paddle_finish_init_params
|
||||
func paddle_finish_init_params(client C.client) C.int {
|
||||
c := get(client)
|
||||
err := c.FinishInitParams()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export paddle_send_grads
|
||||
func paddle_send_grads(client C.client, grads *C.paddle_gradient, total C.int) C.int {
|
||||
var gs []pserver.Gradient
|
||||
for i := 0; i < int(total); i++ {
|
||||
grad := (*C.paddle_gradient)(unsafe.Pointer((uintptr(unsafe.Pointer(grads)) + uintptr(i)*unsafe.Sizeof(*grads))))
|
||||
et := pserver.ElementType(grad.element_type)
|
||||
name := C.GoString(grad.name)
|
||||
content := cArrayToSlice(unsafe.Pointer(grad.content), int(grad.content_len))
|
||||
gs = append(gs, pserver.Gradient{Name: name, ElementType: et, Content: content})
|
||||
}
|
||||
|
||||
c := get(client)
|
||||
err := c.SendGrads(gs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export paddle_get_params
|
||||
func paddle_get_params(client C.client, names **C.char, dst **C.paddle_parameter, total C.int) C.int {
|
||||
var ns []string
|
||||
for i := 0; i < int(total); i++ {
|
||||
name := *(**C.char)(unsafe.Pointer((uintptr(unsafe.Pointer(names)) + uintptr(i)*unsafe.Sizeof(*names))))
|
||||
ns = append(ns, C.GoString(name))
|
||||
}
|
||||
c := get(client)
|
||||
ps, err := c.GetParams(ns)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
for i := 0; i < int(total); i++ {
|
||||
if i >= len(ps) {
|
||||
break
|
||||
}
|
||||
|
||||
p := ps[i]
|
||||
param := *(**C.paddle_parameter)(unsafe.Pointer((uintptr(unsafe.Pointer(dst)) + uintptr(i)*unsafe.Sizeof(*dst))))
|
||||
nameReady := false
|
||||
contentAllocated := false
|
||||
|
||||
if unsafe.Pointer(param) == nullPtr {
|
||||
param = (*C.paddle_parameter)(C.calloc(1, C.size_t(unsafe.Sizeof(*param))))
|
||||
} else {
|
||||
if unsafe.Pointer(param.name) != nullPtr {
|
||||
if n := C.GoString(param.name); n != p.Name {
|
||||
log.Println("Warning: the pre-allocated parameter name does not match the parameter name, it will be freed.", n, p.Name)
|
||||
C.free(unsafe.Pointer(param.name))
|
||||
} else {
|
||||
nameReady = true
|
||||
}
|
||||
}
|
||||
|
||||
if unsafe.Pointer(param.content) != nullPtr {
|
||||
if int(param.content_len) == len(p.Content) {
|
||||
contentAllocated = true
|
||||
} else {
|
||||
log.Println("Warning: the pre-allocated content len does not match parameter content len, the pre-allocated content will be freed.", param.content_len, len(p.Content))
|
||||
C.free(unsafe.Pointer(param.content))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !nameReady {
|
||||
param.name = C.CString(p.Name)
|
||||
}
|
||||
if !contentAllocated {
|
||||
param.content = (*C.uchar)(C.malloc(C.size_t(len(p.Content))))
|
||||
}
|
||||
C.memcpy(unsafe.Pointer(param.content), unsafe.Pointer(&p.Content[0]), C.size_t(len(p.Content)))
|
||||
param.content_len = C.int(len(p.Content))
|
||||
param.element_type = C.paddle_element_type(p.ElementType)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export paddle_save_model
|
||||
func paddle_save_model(client C.client, path *C.char) C.int {
|
||||
p := C.GoString(path)
|
||||
c := get(client)
|
||||
err := c.SaveModel(p)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {} // Required but ignored
|
@ -0,0 +1,44 @@
|
||||
if(NOT CMAKE_Go_COMPILER)
|
||||
if(NOT $ENV{GO_COMPILER} STREQUAL "")
|
||||
get_filename_component(CMAKE_Go_COMPILER_INIT $ENV{GO_COMPILER} PROGRAM PROGRAM_ARGS CMAKE_Go_FLAGS_ENV_INIT)
|
||||
|
||||
if(CMAKE_Go_FLAGS_ENV_INIT)
|
||||
set(CMAKE_Go_COMPILER_ARG1 "${CMAKE_Go_FLAGS_ENV_INIT}" CACHE STRING "First argument to Go compiler")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS ${CMAKE_Go_COMPILER_INIT})
|
||||
message(SEND_ERROR "Could not find compiler set in environment variable GO_COMPILER:\n$ENV{GO_COMPILER}.")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
set(Go_BIN_PATH
|
||||
$ENV{GOPATH}
|
||||
$ENV{GOROOT}
|
||||
$ENV{GOROOT}/../bin
|
||||
$ENV{GO_COMPILER}
|
||||
/usr/bin
|
||||
/usr/local/bin
|
||||
)
|
||||
|
||||
if(CMAKE_Go_COMPILER_INIT)
|
||||
set(CMAKE_Go_COMPILER ${CMAKE_Go_COMPILER_INIT} CACHE PATH "Go Compiler")
|
||||
else()
|
||||
find_program(CMAKE_Go_COMPILER
|
||||
NAMES go
|
||||
PATHS ${Go_BIN_PATH}
|
||||
)
|
||||
EXEC_PROGRAM(${CMAKE_Go_COMPILER} ARGS version OUTPUT_VARIABLE GOLANG_VERSION)
|
||||
STRING(REGEX MATCH "go[0-9]+.[0-9]+.[0-9]+[ /A-Za-z0-9]*" VERSION "${GOLANG_VERSION}")
|
||||
message("-- The Golang compiler identification is ${VERSION}")
|
||||
message("-- Check for working Golang compiler: ${CMAKE_Go_COMPILER}")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
mark_as_advanced(CMAKE_Go_COMPILER)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeGoCompiler.cmake.in
|
||||
${CMAKE_PLATFORM_INFO_DIR}/CMakeGoCompiler.cmake @ONLY)
|
||||
|
||||
set(CMAKE_Go_COMPILER_ENV_VAR "GO_COMPILER")
|
@ -0,0 +1,8 @@
|
||||
set(CMAKE_Go_COMPILER "@CMAKE_Go_COMPILER@")
|
||||
set(CMAKE_Go_COMPILER_LOADED 1)
|
||||
|
||||
set(CMAKE_Go_SOURCE_FILE_EXTENSIONS go)
|
||||
set(CMAKE_Go_LINKER_PREFERENCE 40)
|
||||
set(CMAKE_Go_OUTPUT_EXTENSION .o)
|
||||
set(CMAKE_Go_OUTPUT_EXTENSION_REPLACE 1)
|
||||
set(CMAKE_Go_COMPILER_ENV_VAR "GO_COMPILER")
|
@ -0,0 +1,7 @@
|
||||
if(NOT CMAKE_Go_COMPILE_OBJECT)
|
||||
set(CMAKE_Go_COMPILE_OBJECT "go tool compile -l -N -o <OBJECT> <SOURCE> ")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_Go_LINK_EXECUTABLE)
|
||||
set(CMAKE_Go_LINK_EXECUTABLE "go tool link -o <TARGET> <OBJECTS> ")
|
||||
endif()
|
@ -0,0 +1 @@
|
||||
set(CMAKE_Go_COMPILER_WORKS 1 CACHE INTERNAL "")
|
@ -0,0 +1,45 @@
|
||||
# Setting Paddle Compile Flags
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXSymbolExists)
|
||||
include(CheckTypeSize)
|
||||
|
||||
function(CheckCompilerCXX11Flag)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.8)
|
||||
message(FATAL_ERROR "Unsupported GCC version. GCC >= 4.8 required.")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# cmake >= 3.0 compiler id "AppleClang" on Mac OS X, otherwise "Clang"
|
||||
# Apple Clang is a different compiler than upstream Clang which havs different version numbers.
|
||||
# https://gist.github.com/yamaya/2924292
|
||||
if(APPLE) # cmake < 3.0 compiler id "Clang" on Mac OS X
|
||||
if(${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 5.1)
|
||||
message(FATAL_ERROR "Unsupported AppleClang version. AppleClang >= 5.1 required.")
|
||||
endif()
|
||||
else()
|
||||
if (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.3)
|
||||
message(FATAL_ERROR "Unsupported Clang version. Clang >= 3.3 required.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
CheckCompilerCXX11Flag()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
# Common gpu architectures: Kepler, Maxwell
|
||||
foreach(capability 30 35 50)
|
||||
list(APPEND __arch_flags " -gencode arch=compute_${capability},code=sm_${capability}")
|
||||
endforeach()
|
||||
|
||||
if (CUDA_VERSION VERSION_GREATER "7.0" OR CUDA_VERSION VERSION_EQUAL "7.0")
|
||||
list(APPEND __arch_flags " -gencode arch=compute_52,code=sm_52")
|
||||
endif()
|
||||
|
||||
# Modern gpu architectures: Pascal
|
||||
if (CUDA_VERSION VERSION_GREATER "8.0" OR CUDA_VERSION VERSION_EQUAL "8.0")
|
||||
list(APPEND __arch_flags " -gencode arch=compute_60,code=sm_60")
|
||||
endif()
|
||||
|
||||
set(CUDA_NVCC_FLAGS ${__arch_flags} ${CUDA_NVCC_FLAGS})
|
@ -0,0 +1,46 @@
|
||||
set(GOPATH "${CMAKE_CURRENT_BINARY_DIR}/go")
|
||||
file(MAKE_DIRECTORY ${GOPATH})
|
||||
|
||||
function(ExternalGoProject_Add TARG)
|
||||
add_custom_target(${TARG} env GOPATH=${GOPATH} ${CMAKE_Go_COMPILER} get ${ARGN})
|
||||
endfunction(ExternalGoProject_Add)
|
||||
|
||||
function(add_go_executable NAME)
|
||||
file(GLOB GO_SOURCE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.go")
|
||||
add_custom_command(OUTPUT ${OUTPUT_DIR}/.timestamp
|
||||
COMMAND env GOPATH=${GOPATH} ${CMAKE_Go_COMPILER} build
|
||||
-o "${CMAKE_CURRENT_BINARY_DIR}/${NAME}"
|
||||
${CMAKE_GO_FLAGS} ${GO_SOURCE}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
add_custom_target(${NAME} ALL DEPENDS ${OUTPUT_DIR}/.timestamp ${ARGN})
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${NAME} DESTINATION bin)
|
||||
endfunction(add_go_executable)
|
||||
|
||||
|
||||
function(ADD_GO_LIBRARY NAME BUILD_TYPE)
|
||||
if(BUILD_TYPE STREQUAL "STATIC")
|
||||
set(BUILD_MODE -buildmode=c-archive)
|
||||
set(LIB_NAME "lib${NAME}.a")
|
||||
else()
|
||||
set(BUILD_MODE -buildmode=c-shared)
|
||||
if(APPLE)
|
||||
set(LIB_NAME "lib${NAME}.dylib")
|
||||
else()
|
||||
set(LIB_NAME "lib${NAME}.so")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(GLOB GO_SOURCE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.go")
|
||||
add_custom_command(OUTPUT ${OUTPUT_DIR}/.timestamp
|
||||
COMMAND env GOPATH=${GOPATH} ${CMAKE_Go_COMPILER} build ${BUILD_MODE}
|
||||
-o "${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}"
|
||||
${CMAKE_GO_FLAGS} ${GO_SOURCE}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
add_custom_target(${NAME} ALL DEPENDS ${OUTPUT_DIR}/.timestamp ${ARGN})
|
||||
|
||||
if(NOT BUILD_TYPE STREQUAL "STATIC")
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME} DESTINATION bin)
|
||||
endif()
|
||||
endfunction(ADD_GO_LIBRARY)
|
@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
include_directories(/env/gopath/src/github.com/PaddlePaddle/Paddle/paddle/go/cclient/build/)
|
||||
|
||||
add_executable(main main.c)
|
||||
add_dependencies(main client)
|
||||
set (CMAKE_EXE_LINKER_FLAGS "-pthread")
|
||||
target_link_libraries(main /env/gopath/src/github.com/PaddlePaddle/Paddle/paddle/go/cclient/build/libclient.a) # ${GTEST_LIBRARIES})
|
@ -0,0 +1,69 @@
|
||||
#include "libclient.h"
|
||||
|
||||
//#include "gtest/gtest.h"
|
||||
|
||||
void panic() {
|
||||
// TODO(helin): fix: gtest using cmake is not working, using this
|
||||
// hacky way for now.
|
||||
*(void*)0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char addr[] = "localhost:3000";
|
||||
client c = paddle_new_pserver_client(addr);
|
||||
retry:
|
||||
if (paddle_begin_init_params(c, NULL, 0)) {
|
||||
paddle_parameter param;
|
||||
char name_a[] = "param_a";
|
||||
char name_b[] = "param_b";
|
||||
char content[] = {0x00, 0x11, 0x22};
|
||||
param.element_type = PADDLE_ELEMENT_TYPE_FLOAT32;
|
||||
param.name = name_a;
|
||||
param.content = content;
|
||||
param.content_len = 3;
|
||||
if (paddle_init_param(c, param, NULL, 0) != 0) {
|
||||
goto retry;
|
||||
}
|
||||
param.element_type = PADDLE_ELEMENT_TYPE_INT32;
|
||||
param.name = name_b;
|
||||
param.content = content;
|
||||
param.content_len = 3;
|
||||
if (paddle_init_param(c, param, NULL, 0) != 0) {
|
||||
goto retry;
|
||||
}
|
||||
if (paddle_finish_init_params(c) != 0) {
|
||||
goto retry;
|
||||
}
|
||||
} else {
|
||||
panic();
|
||||
}
|
||||
|
||||
char content[] = {0x00, 0x11, 0x22};
|
||||
paddle_gradient grads[2] = {
|
||||
{"param_a", PADDLE_ELEMENT_TYPE_INT32, content, 3},
|
||||
{"param_b", PADDLE_ELEMENT_TYPE_FLOAT32, content, 3}};
|
||||
|
||||
if (!paddle_send_grads(c, grads, 2)) {
|
||||
panic();
|
||||
}
|
||||
|
||||
paddle_parameter* params[2] = {NULL, NULL};
|
||||
char* names[] = {"param_a", "param_b"};
|
||||
if (!paddle_get_params(c, names, params, 2)) {
|
||||
panic();
|
||||
}
|
||||
|
||||
// get parameters again by reusing the allocated parameter buffers.
|
||||
if (!paddle_get_params(c, names, params, 2)) {
|
||||
panic();
|
||||
}
|
||||
|
||||
paddle_release_param(params[0]);
|
||||
paddle_release_param(params[1]);
|
||||
|
||||
if (!paddle_save_model(c, "/tmp/")) {
|
||||
panic();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package pserver
|
||||
|
||||
// ElementType is the type of elements of a Parameter.
|
||||
type ElementType int
|
||||
|
||||
// Supported element types
|
||||
const (
|
||||
Int32 ElementType = iota
|
||||
UInt32
|
||||
Int64
|
||||
UInt64
|
||||
Float32
|
||||
Float64
|
||||
)
|
||||
|
||||
// Parameter is a piece of data to sync with the parameter server.
|
||||
type Parameter struct {
|
||||
Name string
|
||||
ElementType ElementType
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// ParameterWithConfig contains the parameter and the configuration.
|
||||
type ParameterWithConfig struct {
|
||||
Param Parameter
|
||||
Config []byte // parameter configuration in Proto Buffer format
|
||||
}
|
||||
|
||||
// Gradient is the gradient of the parameter.
|
||||
type Gradient Parameter
|
||||
|
||||
// Client is the client to parameter servers.
|
||||
type Client struct {
|
||||
}
|
||||
|
||||
// NewClient creates a new client.
|
||||
func NewClient(addr string) *Client {
|
||||
return &Client{}
|
||||
}
|
||||
|
||||
// BeginInitParams begins to initialize parameters on parameter
|
||||
// servers.
|
||||
//
|
||||
// BeginInitParams will be called from multiple trainers, only one
|
||||
// trainer will be selected to initialize the parameters on parameter
|
||||
// servers. Other trainers will be blocked until the initialization is
|
||||
// done, and they need to get the initialized parameters from
|
||||
// parameter servers using GetParams.
|
||||
func (c *Client) BeginInitParams(pserverConfigProto []byte) (selected bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// InitParam initializes the parameter on parameter servers.
|
||||
func (c *Client) InitParam(paramWithConfigs ParameterWithConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FinishInitParams tells parameter servers client has sent all
|
||||
// parameters to parameter servers as initialization.
|
||||
func (c *Client) FinishInitParams() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendGrads sends gradients to parameter servers for updating
|
||||
// parameters.
|
||||
func (c *Client) SendGrads(grads []Gradient) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetParams gets parameters from parameter servers.
|
||||
func (c *Client) GetParams(names []string) ([]Parameter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SaveModel indicates parameters to save the parameter to the given
|
||||
// path.
|
||||
func (c *Client) SaveModel(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup cleans up the client states.
|
||||
func (c *Client) Cleanup() {
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
#include "paddle/majel/ddim.h"
|
||||
|
||||
namespace majel {
|
||||
|
||||
///@cond HIDDEN
|
||||
|
||||
template <int i>
|
||||
Dim<i> make_dim(const int* d) {
|
||||
return Dim<i>(*d, make_dim<i - 1>(d + 1));
|
||||
}
|
||||
|
||||
template <>
|
||||
Dim<1> make_dim<1>(const int* d) {
|
||||
return Dim<1>(*d);
|
||||
}
|
||||
|
||||
void make_ddim(DDim& ddim, const int* dims, int n) {
|
||||
switch (n) {
|
||||
case 1:
|
||||
ddim = make_dim<1>(dims);
|
||||
break;
|
||||
case 2:
|
||||
ddim = make_dim<2>(dims);
|
||||
break;
|
||||
case 3:
|
||||
ddim = make_dim<3>(dims);
|
||||
break;
|
||||
case 4:
|
||||
ddim = make_dim<4>(dims);
|
||||
break;
|
||||
case 5:
|
||||
ddim = make_dim<5>(dims);
|
||||
break;
|
||||
case 6:
|
||||
ddim = make_dim<6>(dims);
|
||||
break;
|
||||
case 7:
|
||||
ddim = make_dim<7>(dims);
|
||||
break;
|
||||
case 8:
|
||||
ddim = make_dim<8>(dims);
|
||||
break;
|
||||
case 9:
|
||||
ddim = make_dim<9>(dims);
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument(
|
||||
"Dynamic dimensions must have between [1, 9] dimensions.");
|
||||
}
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
||||
DDim make_ddim(std::initializer_list<int> dims) {
|
||||
DDim result(make_dim(0));
|
||||
make_ddim(result, dims.begin(), dims.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
DDim make_ddim(const std::vector<int>& dims) {
|
||||
DDim result(make_dim(0));
|
||||
make_ddim(result, &dims[0], dims.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
///@cond HIDDEN
|
||||
// XXX For some reason, putting this in an anonymous namespace causes errors
|
||||
class DynamicMutableIndexer : public boost::static_visitor<int&> {
|
||||
public:
|
||||
DynamicMutableIndexer(int idx) : idx_(idx) {}
|
||||
|
||||
template <int D>
|
||||
int& operator()(Dim<D>& dim) const {
|
||||
return dim[idx_];
|
||||
}
|
||||
|
||||
private:
|
||||
int idx_;
|
||||
};
|
||||
|
||||
class DynamicConstIndexer : public boost::static_visitor<int> {
|
||||
public:
|
||||
DynamicConstIndexer(int idx) : idx_(idx) {}
|
||||
|
||||
template <int D>
|
||||
int operator()(const Dim<D>& dim) const {
|
||||
return dim[idx_];
|
||||
}
|
||||
|
||||
private:
|
||||
int idx_;
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
||||
int& DDim::operator[](int idx) {
|
||||
return boost::apply_visitor(DynamicMutableIndexer(idx), var);
|
||||
}
|
||||
|
||||
int DDim::operator[](int idx) const {
|
||||
return boost::apply_visitor(DynamicConstIndexer(idx), var);
|
||||
}
|
||||
|
||||
bool DDim::operator==(DDim d) const {
|
||||
if (var.which() != d.getVar().which()) {
|
||||
return false;
|
||||
} else {
|
||||
std::vector<int> v1 = vectorize(*this);
|
||||
std::vector<int> v2 = vectorize(d);
|
||||
|
||||
for (unsigned int i = 0; i < v1.size(); i++) {
|
||||
if (v1[i] != v2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool DDim::operator!=(DDim d) const { return !(*this == d); }
|
||||
|
||||
DDim DDim::operator+(DDim d) const {
|
||||
std::vector<int> v1 = vectorize(*this);
|
||||
std::vector<int> v2 = vectorize(d);
|
||||
|
||||
std::vector<int> v3;
|
||||
|
||||
assert(v1.size() == v2.size());
|
||||
|
||||
for (unsigned int i = 0; i < v1.size(); i++) {
|
||||
v3.push_back(v1[i] + v2[i]);
|
||||
}
|
||||
|
||||
return make_ddim(v3);
|
||||
}
|
||||
|
||||
DDim DDim::operator*(DDim d) const {
|
||||
std::vector<int> v1 = vectorize(*this);
|
||||
std::vector<int> v2 = vectorize(d);
|
||||
|
||||
std::vector<int> v3;
|
||||
|
||||
assert(v1.size() == v2.size());
|
||||
|
||||
for (unsigned int i = 0; i < v1.size(); i++) {
|
||||
v3.push_back(v1[i] * v2[i]);
|
||||
}
|
||||
|
||||
return make_ddim(v3);
|
||||
}
|
||||
|
||||
int get(const DDim& ddim, int idx) { return ddim[idx]; }
|
||||
|
||||
void set(DDim& ddim, int idx, int value) { ddim[idx] = value; }
|
||||
|
||||
///@cond HIDDEN
|
||||
struct VectorizeVisitor : public boost::static_visitor<> {
|
||||
std::vector<int>& vector;
|
||||
|
||||
VectorizeVisitor(std::vector<int>& v) : vector(v) {}
|
||||
|
||||
template <typename T>
|
||||
void operator()(const T& t) {
|
||||
vector.push_back(t.head);
|
||||
this->operator()(t.tail);
|
||||
}
|
||||
|
||||
void operator()(const Dim<1>& t) { vector.push_back(t.head); }
|
||||
};
|
||||
///@endcond
|
||||
|
||||
std::vector<int> vectorize(const DDim& ddim) {
|
||||
std::vector<int> result;
|
||||
VectorizeVisitor visitor(result);
|
||||
boost::apply_visitor(visitor, ddim);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t product(const DDim& ddim) {
|
||||
ssize_t result = 1;
|
||||
std::vector<int> v = vectorize(ddim);
|
||||
for (auto i : v) {
|
||||
result *= i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
///\cond HIDDEN
|
||||
|
||||
struct ArityVisitor : boost::static_visitor<int> {
|
||||
template <int D>
|
||||
int operator()(Dim<D>) const {
|
||||
return D;
|
||||
}
|
||||
};
|
||||
|
||||
///\endcond
|
||||
|
||||
int arity(const DDim& d) { return boost::apply_visitor(ArityVisitor(), d); }
|
||||
|
||||
///\cond HIDDEN
|
||||
|
||||
struct DDimPrinter : boost::static_visitor<void> {
|
||||
std::ostream& os;
|
||||
DDimPrinter(std::ostream& os_) : os(os_) {}
|
||||
|
||||
template <typename T>
|
||||
void operator()(const T& t) {
|
||||
os << t;
|
||||
}
|
||||
};
|
||||
|
||||
///\endcond
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const majel::DDim& ddim) {
|
||||
DDimPrinter printer(os);
|
||||
boost::apply_visitor(printer, ddim);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace majel
|
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/majel/dim.h"
|
||||
|
||||
namespace majel {
|
||||
|
||||
namespace {
|
||||
typedef boost::variant<Dim<1>,
|
||||
Dim<2>,
|
||||
Dim<3>,
|
||||
Dim<4>,
|
||||
Dim<5>,
|
||||
Dim<6>,
|
||||
Dim<7>,
|
||||
Dim<8>,
|
||||
Dim<9>>
|
||||
DDimVar;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A dynamically sized dimension.
|
||||
*
|
||||
* The number of dimensions must be between [1, 9].
|
||||
*/
|
||||
struct DDim {
|
||||
DDimVar var;
|
||||
|
||||
DDim() : var(Dim<1>()) {}
|
||||
|
||||
template <int D>
|
||||
DDim(const Dim<D>& in) : var(in) {}
|
||||
|
||||
template <int D>
|
||||
DDim& operator=(const Dim<D>& in) {
|
||||
var = in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int& operator[](int idx);
|
||||
int operator[](int idx) const;
|
||||
|
||||
template <typename Visitor>
|
||||
typename Visitor::result_type apply_visitor(Visitor& visitor) {
|
||||
return var.apply_visitor(visitor);
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
typename Visitor::result_type apply_visitor(Visitor& visitor) const {
|
||||
return var.apply_visitor(visitor);
|
||||
}
|
||||
|
||||
DDimVar getVar() { return var; }
|
||||
|
||||
bool operator==(DDim d) const;
|
||||
|
||||
bool operator!=(DDim d) const;
|
||||
|
||||
DDim operator+(DDim d) const;
|
||||
|
||||
DDim operator*(DDim d) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Make a DDim from std::vector<int>
|
||||
*
|
||||
* \param dims An vector of ints. Must be sized between [1, 9]
|
||||
*/
|
||||
DDim make_ddim(const std::vector<int>& dims);
|
||||
|
||||
/**
|
||||
* \brief Make a DDim from an initializer list
|
||||
*
|
||||
* \param dims An initializer list of ints. Must be sized between [1, 9]
|
||||
*
|
||||
*/
|
||||
DDim make_ddim(std::initializer_list<int> dims);
|
||||
|
||||
int get(const DDim& dim, int idx);
|
||||
void set(DDim& dim, int idx, int val);
|
||||
|
||||
std::vector<int> vectorize(const DDim& ddim);
|
||||
|
||||
ssize_t product(const DDim& ddim);
|
||||
|
||||
/**
|
||||
* \brief What is the length of this dimension?
|
||||
*
|
||||
* \param Dynamic dimension to inspect
|
||||
*/
|
||||
|
||||
int arity(const DDim& ddim);
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const majel::DDim&);
|
||||
|
||||
} // namespace majel
|
||||
|
||||
namespace boost {
|
||||
|
||||
template <typename T>
|
||||
T get(const majel::DDim& in) {
|
||||
return boost::get<T>(in.var);
|
||||
}
|
||||
|
||||
} // namespace boost
|
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
#if defined(__APPLE__) && defined(__CUDA_ARCH__) && !defined(NDEBUG)
|
||||
#include <stdio.h>
|
||||
#define MAJEL_ASSERT(e) \
|
||||
do { \
|
||||
if (!(e)) { \
|
||||
printf( \
|
||||
"%s:%d Assertion `%s` failed.\n", __FILE__, __LINE__, TOSTRING(e)); \
|
||||
asm("trap;"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MAJEL_ASSERT_MSG(e, m) \
|
||||
do { \
|
||||
if (!(e)) { \
|
||||
printf("%s:%d Assertion `%s` failed (%s).\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
TOSTRING(e), \
|
||||
m); \
|
||||
asm("trap;"); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#include <assert.h>
|
||||
#define MAJEL_ASSERT(e) assert(e)
|
||||
#define MAJEL_ASSERT_MSG(e, m) assert((e) && (m))
|
||||
#endif
|
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __CUDACC__
|
||||
#define HOSTDEVICE __host__ __device__
|
||||
#define HOST __host__
|
||||
#else
|
||||
#define HOSTDEVICE
|
||||
#define HOST
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,12 @@
|
||||
cc_test(place_test
|
||||
SRCS place_test.cc
|
||||
DEPS majel)
|
||||
DEPS place)
|
||||
|
||||
cc_test(ddim_test
|
||||
SRCS ddim_test.cc
|
||||
DEPS ddim)
|
||||
|
||||
if(WITH_GPU)
|
||||
nv_test(cuda_test SRCS cuda_test.cu)
|
||||
nv_test(dim_test SRCS dim_test.cu DEPS ddim)
|
||||
endif()
|
||||
|
@ -0,0 +1,65 @@
|
||||
//#include <stdexcept>
|
||||
//#include <unittest/unittest.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "paddle/majel/ddim.h"
|
||||
|
||||
TEST(DDim, Equality) {
|
||||
// construct a DDim from an initialization list
|
||||
majel::DDim ddim = majel::make_ddim({9, 1, 5});
|
||||
EXPECT_EQ(ddim[0], 9);
|
||||
EXPECT_EQ(ddim[1], 1);
|
||||
EXPECT_EQ(ddim[2], 5);
|
||||
|
||||
// construct a DDim from a vector
|
||||
std::vector<int> vec({9, 1, 5});
|
||||
majel::DDim vddim = majel::make_ddim(vec);
|
||||
EXPECT_EQ(ddim[0], 9);
|
||||
EXPECT_EQ(ddim[1], 1);
|
||||
EXPECT_EQ(ddim[2], 5);
|
||||
|
||||
// mutate a DDim
|
||||
ddim[1] = 2;
|
||||
EXPECT_EQ(ddim[1], 2);
|
||||
majel::set(ddim, 0, 6);
|
||||
EXPECT_EQ(majel::get(ddim, 0), 6);
|
||||
|
||||
// vectorize a DDim
|
||||
std::vector<int> res_vec = majel::vectorize(vddim);
|
||||
EXPECT_EQ(res_vec[0], 9);
|
||||
EXPECT_EQ(res_vec[1], 1);
|
||||
EXPECT_EQ(res_vec[2], 5);
|
||||
majel::Dim<3> d(3, 2, 1);
|
||||
res_vec = majel::vectorize(majel::DDim(d));
|
||||
EXPECT_EQ(res_vec[0], 3);
|
||||
EXPECT_EQ(res_vec[1], 2);
|
||||
EXPECT_EQ(res_vec[2], 1);
|
||||
|
||||
// add two DDims
|
||||
majel::DDim ddim_sum = ddim + vddim;
|
||||
EXPECT_EQ(ddim_sum[0], 15);
|
||||
EXPECT_EQ(ddim_sum[1], 3);
|
||||
EXPECT_EQ(ddim_sum[2], 10);
|
||||
|
||||
// multiply two DDims
|
||||
majel::DDim ddim_mul = ddim * vddim;
|
||||
EXPECT_EQ(ddim_mul[0], 54);
|
||||
EXPECT_EQ(ddim_mul[1], 2);
|
||||
EXPECT_EQ(ddim_mul[2], 25);
|
||||
|
||||
// arity of a DDim
|
||||
EXPECT_EQ(majel::arity(ddim), 3);
|
||||
|
||||
// product of a DDim
|
||||
EXPECT_EQ(majel::product(vddim), 45);
|
||||
}
|
||||
|
||||
TEST(DDim, Print) {
|
||||
// print a DDim
|
||||
std::stringstream ss;
|
||||
majel::DDim ddim = majel::make_ddim({2, 3, 4});
|
||||
ss << ddim;
|
||||
EXPECT_EQ("2, 3, 4", ss.str());
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue