提交 8b824a98 编写于 作者: Y yejianwu

separate opencl when only run on cpu

上级 3713852b
......@@ -351,3 +351,24 @@ Use ``-h`` to get detailed help.
python tools/converter.py build -h
python tools/converter.py run -h
python tools/converter.py benchmark -h
Reduce Library Size
-------------------
* **dynamic library**
The generated dynamic library by script ``tools/build-standalone-lib.sh`` is about ``1.6M`` for
``armeabi-v7a`` and ``2.1M`` for ``arm64-v8a``. It can be reduced by modifying some build options.
- If the models don't need to run on device ``dsp``, the build option ``--define hexagon=true`` for
``armeabi-v7a`` can be set to ``false``. And the library will be decreased about ``100KB``.
- Futher more, if only ``cpu`` device needed, set ``--define opencl=true`` to ``false``. This way
will reduce half of library size to about ``700KB`` for ``armeabi-v7a`` and ``1000KB`` for ``arm64-v8a``
* **static library**
- The methods in dynamic library can be useful for static library too. In additional, the static
library may also contain model graph and model datas if the configs ``model_graph_format`` and
``model_data_format`` in deployment file are set to ``code``.
- It is recommended to use ``version script`` for ultimate binary. The effect is remarkable.
......@@ -52,56 +52,10 @@ config_setting(
visibility = ["//visibility:public"],
)
cc_binary(
name = "libmace.so",
linkopts = [
"-Wl,-soname,libmace.so",
"-Wl,--version-script",
"mace_version_script.lds",
],
linkshared = 1,
linkstatic = 0,
deps = [
":mace_version_script.lds",
"//mace/libmace",
],
)
cc_library(
name = "libmace",
srcs = ["libmace.so"],
visibility = ["//visibility:public"],
)
genrule(
name = "libmace_static",
srcs = [
"//mace/codegen:generated_opencl",
"//mace/codegen:generated_version",
"//mace/core",
"//mace/kernels",
"//mace/ops",
"//mace/libmace",
"//mace/utils",
"//mace/proto:mace_cc",
"@com_google_protobuf//:protobuf_lite",
],
outs = ["libmace.a"],
cmd = "tmp_mri_file=$$(mktemp mace-static-lib-mri.XXXXXXXXXX);" +
"mri_stream=$$(python $(location //mace/python/tools:archive_static_lib) " +
"$(locations //mace/codegen:generated_opencl) " +
"$(locations //mace/codegen:generated_version) " +
"$(locations //mace/core:core) " +
"$(locations //mace/kernels:kernels) " +
"$(locations //mace/ops:ops) " +
"$(locations //mace/libmace:libmace) " +
"$(locations //mace/utils:utils) " +
"$(locations //mace/proto:mace_cc) " +
"$(locations @com_google_protobuf//:protobuf_lite) " +
"$@ " +
"$$tmp_mri_file);" +
"$(AR) -M <$$tmp_mri_file;" +
"rm -rf $$tmp_mri_file;",
tools = ["//mace/python/tools:archive_static_lib"],
config_setting(
name = "opencl_enabled",
define_values = {
"opencl": "true",
},
visibility = ["//visibility:public"],
)
......@@ -5,6 +5,7 @@ load(
"if_hexagon_enabled",
"if_openmp_enabled",
"if_android",
"if_opencl_enabled",
)
licenses(["notice"]) # Apache 2.0
......@@ -28,7 +29,7 @@ cc_binary(
"-Werror",
"-Wextra",
"-Wno-missing-field-initializers",
] + if_android(["-DMACE_ENABLE_OPENCL"]),
] + if_opencl_enabled(["-DMACE_ENABLE_OPENCL"]),
linkopts = if_openmp_enabled(["-fopenmp"]),
linkstatic = 1,
deps = [
......@@ -56,7 +57,7 @@ cc_binary(
":statistics",
"//external:gflags_nothreads",
"//mace/codegen:generated_mace_engine_factory",
"//mace:libmace",
"//mace/libmace:libmace_dynamic",
],
)
......
......@@ -14,6 +14,7 @@ load(
"if_not_hexagon_enabled",
"if_openmp_enabled",
"if_neon_enabled",
"if_opencl_enabled",
)
cc_library(
......@@ -26,7 +27,7 @@ cc_library(
exclude = [
"*_test.cc",
],
) + if_android(glob(
) + if_opencl_enabled(glob(
[
"runtime/opencl/*.cc",
],
......@@ -36,7 +37,7 @@ cc_library(
hdrs = glob([
"*.h",
"runtime/cpu/*.h",
]) + if_android(glob(
]) + if_opencl_enabled(glob(
[
"runtime/opencl/*.h",
],
......@@ -48,7 +49,7 @@ cc_library(
] + if_openmp_enabled([
"-fopenmp",
"-DMACE_ENABLE_OPENMP",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -63,7 +64,7 @@ cc_library(
"//mace/codegen:generated_version",
"//mace/proto:mace_cc",
"//mace/utils",
] + if_android([
] + if_opencl_enabled([
":opencl_headers",
"//mace/codegen:generated_opencl",
"@half//:half",
......
......@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mace/core/file_storage.h"
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
......@@ -25,36 +23,12 @@
#include <memory>
#include <utility>
#include "mace/core/file_storage.h"
#include "mace/utils/logging.h"
namespace mace {
class FileStorageFactory::Impl {
public:
explicit Impl(const std::string &path);
std::unique_ptr<KVStorage> CreateStorage(const std::string &name);
private:
std::string path_;
};
FileStorageFactory::Impl::Impl(const std::string &path): path_(path) {}
std::unique_ptr<KVStorage> FileStorageFactory::Impl::CreateStorage(
const std::string &name) {
return std::move(std::unique_ptr<KVStorage>(
new FileStorage(path_ + "/" + name)));
}
FileStorageFactory::FileStorageFactory(const std::string &path):
impl_(new FileStorageFactory::Impl(path)) {}
FileStorageFactory::~FileStorageFactory() = default;
std::unique_ptr<KVStorage> FileStorageFactory::CreateStorage(
const std::string &name) {
return impl_->CreateStorage(name);
}
std::shared_ptr<KVStorageFactory> kStorageFactory = nullptr;
FileStorage::FileStorage(const std::string &file_path):
data_changed_(false), file_path_(file_path) {}
......
......@@ -33,8 +33,6 @@
namespace mace {
std::shared_ptr<KVStorageFactory> kStorageFactory = nullptr;
std::string kOpenCLParameterPath; // NOLINT(runtime/string)
extern const std::map<std::string, std::vector<unsigned char>>
......
# Examples
load("//mace:mace.bzl", "if_openmp_enabled", "if_android", "if_hexagon_enabled")
load(
"//mace:mace.bzl",
"if_openmp_enabled",
"if_android",
"if_hexagon_enabled",
"if_opencl_enabled",
)
cc_binary(
name = "example_static",
......@@ -7,7 +13,7 @@ cc_binary(
copts = [
"-Werror",
"-Wextra",
] + if_android([
] + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]),
linkopts = [
......@@ -36,7 +42,7 @@ cc_binary(
"-Werror",
"-Wextra",
"-Wno-missing-field-initializers",
] + if_android([
] + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]),
linkopts = [
......
......@@ -7,7 +7,15 @@ package(
licenses(["notice"]) # Apache 2.0
load("//mace:mace.bzl", "if_android", "if_neon_enabled", "if_openmp_enabled", "if_android_armv7", "if_hexagon_enabled")
load(
"//mace:mace.bzl",
"if_android",
"if_neon_enabled",
"if_openmp_enabled",
"if_android_armv7",
"if_hexagon_enabled",
"if_opencl_enabled",
)
cc_library(
name = "kernels",
......@@ -21,7 +29,7 @@ cc_library(
"*_benchmark.cc",
"arm/*_test.cc",
],
) + if_android(glob(
) + if_opencl_enabled(glob(
[
"opencl/*.cc",
],
......@@ -37,7 +45,7 @@ cc_library(
exclude = [
"buffer_to_image.h",
],
) + if_android(glob([
) + if_opencl_enabled(glob([
"opencl/*.h",
"buffer_to_image.h",
])),
......@@ -53,7 +61,7 @@ cc_library(
"-mfpu=neon",
]) + if_android_armv7([
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -86,7 +94,7 @@ cc_test(
]) + if_android_armv7([
"-mfpu=neon",
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -116,7 +124,7 @@ cc_test(
]) + if_android_armv7([
"-mfpu=neon",
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......
......@@ -7,7 +7,15 @@ package(
licenses(["notice"]) # Apache 2.0
load("//mace:mace.bzl", "if_android", "if_neon_enabled", "if_openmp_enabled", "if_android_armv7", "if_hexagon_enabled")
load(
"//mace:mace.bzl",
"if_android",
"if_neon_enabled",
"if_openmp_enabled",
"if_android_armv7",
"if_hexagon_enabled",
"if_opencl_enabled",
)
cc_library(
name = "libmace",
......@@ -23,7 +31,7 @@ cc_library(
"-mfpu=neon",
]) + if_android_armv7([
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -34,3 +42,57 @@ cc_library(
],
alwayslink = 1,
)
cc_binary(
name = "libmace.so",
linkopts = [
"-Wl,-soname,libmace.so",
"-Wl,--version-script",
"mace_version_script.lds",
],
linkshared = 1,
linkstatic = 0,
deps = [
":mace_version_script.lds",
"//mace/libmace",
],
)
cc_library(
name = "libmace_dynamic",
srcs = ["libmace.so"],
visibility = ["//visibility:public"],
)
genrule(
name = "libmace_static",
srcs = [
"//mace/codegen:generated_opencl",
"//mace/codegen:generated_version",
"//mace/core",
"//mace/kernels",
"//mace/ops",
"//mace/libmace",
"//mace/utils",
"//mace/proto:mace_cc",
"@com_google_protobuf//:protobuf_lite",
],
outs = ["libmace.a"],
cmd = "tmp_mri_file=$$(mktemp mace-static-lib-mri.XXXXXXXXXX);" +
"mri_stream=$$(python $(location //mace/python/tools:archive_static_lib) " +
"$(locations //mace/codegen:generated_opencl) " +
"$(locations //mace/codegen:generated_version) " +
"$(locations //mace/core:core) " +
"$(locations //mace/kernels:kernels) " +
"$(locations //mace/ops:ops) " +
"$(locations //mace/libmace:libmace) " +
"$(locations //mace/utils:utils) " +
"$(locations //mace/proto:mace_cc) " +
"$(locations @com_google_protobuf//:protobuf_lite) " +
"$@ " +
"$$tmp_mri_file);" +
"$(AR) -M <$$tmp_mri_file;" +
"rm -rf $$tmp_mri_file;",
tools = ["//mace/python/tools:archive_static_lib"],
visibility = ["//visibility:public"],
)
......@@ -12,13 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mace/core/file_storage.h"
#include "mace/core/runtime/cpu/cpu_runtime.h"
#include "mace/core/runtime/opencl/opencl_runtime.h"
#include "mace/public/mace_runtime.h"
#include "mace/utils/logging.h"
#ifdef MACE_ENABLE_OPENCL
#include "mace/core/runtime/opencl/opencl_runtime.h"
#endif // MACE_ENABLE_OPENCL
namespace mace {
class FileStorageFactory::Impl {
public:
explicit Impl(const std::string &path);
std::unique_ptr<KVStorage> CreateStorage(const std::string &name);
private:
std::string path_;
};
FileStorageFactory::Impl::Impl(const std::string &path): path_(path) {}
std::unique_ptr<KVStorage> FileStorageFactory::Impl::CreateStorage(
const std::string &name) {
return std::move(std::unique_ptr<KVStorage>(
new FileStorage(path_ + "/" + name)));
}
FileStorageFactory::FileStorageFactory(const std::string &path):
impl_(new FileStorageFactory::Impl(path)) {}
FileStorageFactory::~FileStorageFactory() = default;
std::unique_ptr<KVStorage> FileStorageFactory::CreateStorage(
const std::string &name) {
return impl_->CreateStorage(name);
}
extern std::shared_ptr<KVStorageFactory> kStorageFactory;
void SetKVStorageFactory(std::shared_ptr<KVStorageFactory> storage_factory) {
......@@ -26,6 +57,7 @@ void SetKVStorageFactory(std::shared_ptr<KVStorageFactory> storage_factory) {
kStorageFactory = storage_factory;
}
#ifdef MACE_ENABLE_OPENCL
// Set OpenCL Compiled Binary paths, just call once. (Not thread-safe)
void SetOpenCLBinaryPaths(const std::vector<std::string> &paths) {
OpenCLRuntime::ConfigureOpenCLBinaryPath(paths);
......@@ -42,6 +74,7 @@ void SetGPUHints(GPUPerfHint gpu_perf_hint, GPUPriorityHint gpu_priority_hint) {
<< ", gpu_priority_hint: " << gpu_priority_hint;
OpenCLRuntime::Configure(gpu_perf_hint, gpu_priority_hint);
}
#endif // MACE_ENABLE_OPENCL
MaceStatus SetOpenMPThreadPolicy(int num_threads_hint,
CPUAffinityPolicy policy) {
......
......@@ -48,6 +48,11 @@ def if_openmp_enabled(a):
"//conditions:default": [],
})
def if_opencl_enabled(a):
return select({
"//mace:opencl_enabled": a,
"//conditions:default": [],
})
def mace_version_genrule():
native.genrule(
......
......@@ -7,7 +7,15 @@ package(
licenses(["notice"]) # Apache 2.0
load("//mace:mace.bzl", "if_android", "if_neon_enabled", "if_openmp_enabled", "if_android_armv7", "if_hexagon_enabled")
load(
"//mace:mace.bzl",
"if_android",
"if_neon_enabled",
"if_openmp_enabled",
"if_android_armv7",
"if_hexagon_enabled",
"if_opencl_enabled",
)
cc_library(
name = "test",
......@@ -31,7 +39,7 @@ cc_library(
"buffer_to_image.cc",
"image_to_buffer.cc",
],
) + if_android(
) + if_opencl_enabled(
[
"buffer_to_image.cc",
"image_to_buffer.cc",
......@@ -50,7 +58,7 @@ cc_library(
"-mfpu=neon",
]) + if_android_armv7([
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -75,7 +83,7 @@ cc_test(
"-mfpu=neon",
]) + if_android_armv7([
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......@@ -102,7 +110,7 @@ cc_test(
"-mfpu=neon",
]) + if_android_armv7([
"-mfloat-abi=softfp",
]) + if_android([
]) + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]) + if_hexagon_enabled([
"-DMACE_ENABLE_HEXAGON",
......
# Examples
load("//mace:mace.bzl", "if_openmp_enabled", "if_android")
load("//mace:mace.bzl", "if_openmp_enabled", "if_android", "if_opencl_enabled")
cc_binary(
name = "mace_run_static",
......@@ -7,7 +7,7 @@ cc_binary(
copts = [
"-Werror",
"-Wextra",
] + if_android([
] + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]),
linkopts = if_openmp_enabled(["-fopenmp"]),
......@@ -26,7 +26,7 @@ cc_binary(
copts = [
"-Werror",
"-Wextra",
] + if_android([
] + if_opencl_enabled([
"-DMACE_ENABLE_OPENCL",
]),
linkopts = ["-lm", "-pie", "-fPIE"] + if_openmp_enabled(["-fopenmp"]),
......@@ -34,7 +34,7 @@ cc_binary(
deps = [
"//external:gflags_nothreads",
"//mace/codegen:generated_mace_engine_factory",
"//mace:libmace",
"//mace/libmace:libmace_dynamic",
"//mace/utils:utils",
],
)
......@@ -23,32 +23,32 @@ mkdir -p $LIB_DIR/linux-x86-64
# build shared libraries
echo "build shared lib for armeabi-v7a"
bazel build --config android --config optimization mace:libmace --define neon=true --define openmp=true --define hexagon=true --cpu=armeabi-v7a
cp bazel-bin/mace/libmace.so $LIB_DIR/armeabi-v7a/
bazel build --config android --config optimization mace/libmace:libmace_dynamic --define neon=true --define openmp=true --define opencl=true --define hexagon=true --cpu=armeabi-v7a
cp bazel-bin/mace/libmace/libmace.so $LIB_DIR/armeabi-v7a/
cp third_party/nnlib/*so $LIB_DIR/armeabi-v7a/
echo "build shared lib for arm64-v8a"
bazel build --config android --config optimization mace:libmace --define neon=true --define openmp=true --cpu=arm64-v8a
cp bazel-bin/mace/libmace.so $LIB_DIR/arm64-v8a/
bazel build --config android --config optimization mace/libmace:libmace_dynamic --define neon=true --define openmp=true --define opencl=true --cpu=arm64-v8a
cp bazel-bin/mace/libmace/libmace.so $LIB_DIR/arm64-v8a/
echo "build shared lib for linux-x86-64"
bazel build mace:libmace --config optimization --define openmp=true
cp bazel-bin/mace/libmace.so $LIB_DIR/linux-x86-64/
bazel build mace/libmace:libmace_dynamic --config optimization --define openmp=true
cp bazel-bin/mace/libmace/libmace.so $LIB_DIR/linux-x86-64/
# build static libraries
echo "build static lib for armeabi-v7a"
bazel build --config android --config optimization mace:libmace_static --define neon=true --define openmp=true --define hexagon=true --cpu=armeabi-v7a
cp bazel-genfiles/mace/libmace.a $LIB_DIR/armeabi-v7a/
bazel build --config android --config optimization mace/libmace:libmace_static --define neon=true --define openmp=true --define opencl=true --define hexagon=true --cpu=armeabi-v7a
cp bazel-genfiles/mace/libmace/libmace.a $LIB_DIR/armeabi-v7a/
cp third_party/nnlib/*so $LIB_DIR/armeabi-v7a/
echo "build static lib for arm64-v8a"
bazel build --config android --config optimization mace:libmace_static --define neon=true --define openmp=true --cpu=arm64-v8a
cp bazel-genfiles/mace/libmace.a $LIB_DIR/arm64-v8a/
bazel build --config android --config optimization mace/libmace:libmace_static --define neon=true --define openmp=true --define opencl=true --cpu=arm64-v8a
cp bazel-genfiles/mace/libmace/libmace.a $LIB_DIR/arm64-v8a/
echo "build static lib for linux-x86-64"
bazel build mace:libmace_static --config optimization --define openmp=true
cp bazel-genfiles/mace/libmace.a $LIB_DIR/linux-x86-64/
bazel build mace/libmace:libmace_static --config optimization --define openmp=true
cp bazel-genfiles/mace/libmace/libmace.a $LIB_DIR/linux-x86-64/
echo "LIB PATH: $LIB_DIR"
echo "INCLUDE FILE PATH: $INCLUDE_DIR"
......@@ -61,10 +61,10 @@ CODEGEN_BASE_DIR = 'mace/codegen'
MODEL_CODEGEN_DIR = CODEGEN_BASE_DIR + '/models'
ENGINE_CODEGEN_DIR = CODEGEN_BASE_DIR + '/engine'
LIB_CODEGEN_DIR = CODEGEN_BASE_DIR + '/lib'
LIBMACE_SO_TARGET = "//mace:libmace.so"
LIBMACE_STATIC_TARGET = "//mace:libmace_static"
LIBMACE_STATIC_PATH = "bazel-genfiles/mace/libmace.a"
LIBMACE_DYNAMIC_PATH = "bazel-bin/mace/libmace.so"
LIBMACE_SO_TARGET = "//mace/libmace:libmace.so"
LIBMACE_STATIC_TARGET = "//mace/libmace:libmace_static"
LIBMACE_STATIC_PATH = "bazel-genfiles/mace/libmace/libmace.a"
LIBMACE_DYNAMIC_PATH = "bazel-bin/mace/libmace/libmace.so"
MODEL_LIB_TARGET = "//mace/codegen:generated_models"
MODEL_LIB_PATH = "bazel-bin/mace/codegen/libgenerated_models.a"
MACE_RUN_STATIC_NAME = "mace_run_static"
......@@ -248,6 +248,19 @@ def get_hexagon_mode(configs):
return False
def get_opencl_mode(configs):
runtime_list = []
for model_name in configs[YAMLKeyword.models]:
model_runtime =\
configs[YAMLKeyword.models][model_name].get(
YAMLKeyword.runtime, "")
runtime_list.append(model_runtime.lower())
if RuntimeType.gpu in runtime_list or RuntimeType.cpu_gpu in runtime_list:
return True
return False
def md5sum(str):
md5 = hashlib.md5()
md5.update(str)
......@@ -741,6 +754,7 @@ def build_model_lib(configs, address_sanitizer):
MODEL_LIB_TARGET,
abi=target_abi,
hexagon_mode=hexagon_mode,
enable_opencl=get_opencl_mode(configs),
address_sanitizer=address_sanitizer
)
......@@ -850,6 +864,7 @@ def build_mace_run(configs, target_abi, enable_openmp, address_sanitizer,
abi=target_abi,
hexagon_mode=hexagon_mode,
enable_openmp=enable_openmp,
enable_opencl=get_opencl_mode(configs),
address_sanitizer=address_sanitizer,
extra_args=build_arg
)
......@@ -873,6 +888,7 @@ def build_example(configs, target_abi, enable_openmp, mace_lib_type):
sh_commands.bazel_build(libmace_target,
abi=target_abi,
enable_openmp=enable_openmp,
enable_opencl=get_opencl_mode(configs),
hexagon_mode=hexagon_mode)
if os.path.exists(LIB_CODEGEN_DIR):
......@@ -899,6 +915,7 @@ def build_example(configs, target_abi, enable_openmp, mace_lib_type):
sh_commands.bazel_build(example_target,
abi=target_abi,
enable_openmp=enable_openmp,
enable_opencl=get_opencl_mode(configs),
hexagon_mode=hexagon_mode,
extra_args=build_arg)
......@@ -1245,6 +1262,7 @@ def build_benchmark_model(configs, target_abi, enable_openmp, mace_lib_type):
sh_commands.bazel_build(benchmark_target,
abi=target_abi,
enable_openmp=enable_openmp,
enable_opencl=get_opencl_mode(configs),
hexagon_mode=hexagon_mode,
extra_args=build_arg)
# clear tmp binary dir
......
......@@ -282,6 +282,7 @@ def bazel_build(target,
hexagon_mode=False,
enable_openmp=True,
enable_neon=True,
enable_opencl=True,
address_sanitizer=False,
extra_args=""):
print("* Build %s with ABI %s" % (target, abi))
......@@ -304,6 +305,8 @@ def bazel_build(target,
"--define",
"openmp=%s" % str(enable_openmp).lower(),
"--define",
"opencl=%s" % str(enable_opencl).lower(),
"--define",
"hexagon=%s" % str(hexagon_mode).lower())
if address_sanitizer:
bazel_args += ("--config", "asan")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册