blob: d9299832ddb3f250396d15fb713220205b67418d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file cpu_device_api.cc
*/
#include <dmlc/thread_local.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/runtime/device_api.h>
#include <tvm/runtime/logging.h>
#include <cstdlib>
#include <cstring>
#include "workspace_pool.h"
#ifdef __ANDROID__
#include <android/api-level.h>
#endif
#if defined(__linux__) || defined(__ANDROID__)
#include <sys/sysinfo.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#include <sys/sysctl.h>
#endif
namespace tvm {
namespace runtime {
class CPUDeviceAPI final : public DeviceAPI {
public:
void SetDevice(Device dev) final {}
void GetAttr(Device dev, DeviceAttrKind kind, ffi::Any* rv) final {
if (kind == kExist) {
*rv = 1;
}
switch (kind) {
case kExist:
break;
case kTotalGlobalMemory: {
#if defined(__linux__) || defined(__ANDROID__)
struct sysinfo info;
if (sysinfo(&info) == 0) {
*rv = static_cast<int64_t>(info.totalram) * info.mem_unit; // Convert to bytes
} else {
*rv = -1;
}
#elif defined(_WIN32)
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
*rv = static_cast<int64_t>(statex.ullTotalPhys); // Total physical memory in bytes
} else {
*rv = -1;
}
#elif defined(__APPLE__)
int64_t mem;
size_t size = sizeof(mem);
if (sysctlbyname("hw.memsize", &mem, &size, nullptr, 0) == 0) {
*rv = mem;
} else {
*rv = -1;
}
#else
*rv = -1;
#endif
}
default:
break;
}
}
void* AllocDataSpace(Device dev, size_t nbytes, size_t alignment, DLDataType type_hint) final {
void* ptr;
#if _MSC_VER
ptr = _aligned_malloc(nbytes, alignment);
if (ptr == nullptr) throw std::bad_alloc();
#elif defined(__ANDROID__) && __ANDROID_API__ < 17
ptr = memalign(alignment, nbytes);
if (ptr == nullptr) throw std::bad_alloc();
#else
// posix_memalign is available in android ndk since __ANDROID_API__ >= 17
int ret = posix_memalign(&ptr, alignment, nbytes);
if (ret != 0) throw std::bad_alloc();
#endif
return ptr;
}
void FreeDataSpace(Device dev, void* ptr) final {
#if _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
}
void StreamSync(Device dev, TVMStreamHandle stream) final {}
void* AllocWorkspace(Device dev, size_t size, DLDataType type_hint) final;
void FreeWorkspace(Device dev, void* data) final;
bool SupportsDevicePointerArithmeticsOnHost() final { return true; }
static CPUDeviceAPI* Global() {
// NOTE: explicitly use new to avoid exit-time destruction of global state
// Global state will be recycled by OS as the process exits.
static auto* inst = new CPUDeviceAPI();
return inst;
}
protected:
void CopyDataFromTo(const void* from, size_t from_offset, void* to, size_t to_offset, size_t size,
Device dev_from, Device dev_to, DLDataType type_hint,
TVMStreamHandle stream) final {
memcpy(static_cast<char*>(to) + to_offset, static_cast<const char*>(from) + from_offset, size);
}
};
struct CPUWorkspacePool : public WorkspacePool {
CPUWorkspacePool() : WorkspacePool(kDLCPU, CPUDeviceAPI::Global()) {}
};
void* CPUDeviceAPI::AllocWorkspace(Device dev, size_t size, DLDataType type_hint) {
return dmlc::ThreadLocalStore<CPUWorkspacePool>::Get()->AllocWorkspace(dev, size);
}
void CPUDeviceAPI::FreeWorkspace(Device dev, void* data) {
dmlc::ThreadLocalStore<CPUWorkspacePool>::Get()->FreeWorkspace(dev, data);
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef().def_packed("device_api.cpu", [](ffi::PackedArgs args, ffi::Any* rv) {
DeviceAPI* ptr = CPUDeviceAPI::Global();
*rv = static_cast<void*>(ptr);
});
}
} // namespace runtime
} // namespace tvm