chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by raver119 on 21.11.17.
|
||||
//
|
||||
#include <array/ByteOrderUtils.h>
|
||||
|
||||
namespace sd {
|
||||
ByteOrder ByteOrderUtils::fromFlatByteOrder(graph::ByteOrder order) { return (ByteOrder)order; }
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,72 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/ConstantDataBuffer.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
|
||||
namespace sd {
|
||||
ConstantDataBuffer::ConstantDataBuffer(const std::shared_ptr<PointerWrapper>& primary, uint64_t numEelements,
|
||||
DataType dtype)
|
||||
: ConstantDataBuffer(primary, {}, numEelements, dtype) {
|
||||
//
|
||||
}
|
||||
|
||||
ConstantDataBuffer::ConstantDataBuffer(const std::shared_ptr<PointerWrapper>& primary,
|
||||
const std::shared_ptr<PointerWrapper>& special, uint64_t numEelements,
|
||||
DataType dtype)
|
||||
: _primaryBuffer(primary), _specialBuffer(special), _length(numEelements) {
|
||||
_sizeOf = DataTypeUtils::sizeOf(dtype);
|
||||
}
|
||||
|
||||
void* ConstantDataBuffer::primary() const { return _primaryBuffer->pointer(); }
|
||||
|
||||
void* ConstantDataBuffer::special() const { return _specialBuffer ? _specialBuffer->pointer() : nullptr; }
|
||||
|
||||
uint8_t ConstantDataBuffer::sizeOf() const { return _sizeOf; }
|
||||
|
||||
uint64_t ConstantDataBuffer::length() const { return _length; }
|
||||
|
||||
ConstantDataBuffer::ConstantDataBuffer(const ConstantDataBuffer& other) {
|
||||
_primaryBuffer = other._primaryBuffer;
|
||||
_specialBuffer = other._specialBuffer;
|
||||
_length = other._length;
|
||||
_sizeOf = other._sizeOf;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* ConstantDataBuffer::primaryAsT() const {
|
||||
return reinterpret_cast<T*>(_primaryBuffer->pointer());
|
||||
}
|
||||
template SD_LIB_EXPORT float* ConstantDataBuffer::primaryAsT<float>() const;
|
||||
template SD_LIB_EXPORT double* ConstantDataBuffer::primaryAsT<double>() const;
|
||||
template SD_LIB_EXPORT int* ConstantDataBuffer::primaryAsT<int>() const;
|
||||
template SD_LIB_EXPORT LongType* ConstantDataBuffer::primaryAsT<LongType>() const;
|
||||
|
||||
template <typename T>
|
||||
T* ConstantDataBuffer::specialAsT() const {
|
||||
return reinterpret_cast<T*>(special());
|
||||
}
|
||||
template SD_LIB_EXPORT float* ConstantDataBuffer::specialAsT<float>() const;
|
||||
template SD_LIB_EXPORT double* ConstantDataBuffer::specialAsT<double>() const;
|
||||
template SD_LIB_EXPORT int* ConstantDataBuffer::specialAsT<int>() const;
|
||||
template SD_LIB_EXPORT LongType* ConstantDataBuffer::specialAsT<LongType>() const;
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,79 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/ConstantDescriptor.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <helpers/ModularHasher.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sd {
|
||||
ConstantDescriptor::ConstantDescriptor(double *values, int length) {
|
||||
for (int e = 0; e < length; e++) _floatValues.emplace_back(values[e]);
|
||||
}
|
||||
|
||||
ConstantDescriptor::ConstantDescriptor(LongType const *values, int length) {
|
||||
for (int e = 0; e < length; e++) _integerValues.emplace_back(values[e]);
|
||||
}
|
||||
|
||||
ConstantDescriptor::ConstantDescriptor(std::initializer_list<double> values) { _floatValues = values; }
|
||||
|
||||
ConstantDescriptor::ConstantDescriptor(std::vector<LongType> &values) { _integerValues = values; }
|
||||
|
||||
ConstantDescriptor::ConstantDescriptor(std::vector<double> &values) { _floatValues = values; }
|
||||
|
||||
// equal to operator
|
||||
bool ConstantDescriptor::operator==(const ConstantDescriptor &other) const {
|
||||
return std::tie(_floatValues, _integerValues) == std::tie(other._floatValues, other._integerValues);
|
||||
}
|
||||
|
||||
// less than operator
|
||||
bool ConstantDescriptor::operator<(const ConstantDescriptor &other) const {
|
||||
return std::tie(_floatValues, _integerValues) < std::tie(other._floatValues, other._integerValues);
|
||||
}
|
||||
|
||||
bool ConstantDescriptor::isInteger() const { return !_integerValues.empty(); }
|
||||
|
||||
bool ConstantDescriptor::isFloat() const { return !_floatValues.empty(); }
|
||||
|
||||
const std::vector<LongType> &ConstantDescriptor::integerValues() const { return _integerValues; }
|
||||
|
||||
const std::vector<double> &ConstantDescriptor::floatValues() const { return _floatValues; }
|
||||
|
||||
LongType ConstantDescriptor::length() const {
|
||||
return isInteger() ? _integerValues.size() : isFloat() ? _floatValues.size() : 0L;
|
||||
}
|
||||
} // namespace sd
|
||||
|
||||
namespace std {
|
||||
size_t hash<sd::ConstantDescriptor>::operator()(const sd::ConstantDescriptor &k) const {
|
||||
uint64_t hash = sd::helpers::detail::ModularHasher::hash_scalar(k.isInteger());
|
||||
|
||||
// Hash the appropriate vector based on type
|
||||
if (k.isInteger()) {
|
||||
hash = sd::helpers::detail::ModularHasher::hash_vector(k.integerValues(), hash);
|
||||
} else {
|
||||
hash = sd::helpers::detail::ModularHasher::hash_vector(k.floatValues(), hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
|
||||
}
|
||||
} // namespace std
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2019 Konduit K.K.
|
||||
*
|
||||
******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by raver on 5/17/2019.
|
||||
//
|
||||
#include <array/ConstantHolder.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <helpers/shape.h>
|
||||
|
||||
namespace sd {
|
||||
ConstantHolder::ConstantHolder(const ConstantHolder& other) {
|
||||
_buffers = other._buffers;
|
||||
_deviceId = other._deviceId;
|
||||
}
|
||||
|
||||
bool ConstantHolder::hasBuffer(DataType dataType) { return _buffers.count(dataType) > 0; }
|
||||
|
||||
std::mutex* ConstantHolder::mutex() { return &_mutex; }
|
||||
|
||||
template <typename T>
|
||||
bool ConstantHolder::hasBuffer() {
|
||||
return hasBuffer(DataTypeUtils::fromT<T>());
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT bool ConstantHolder::hasBuffer, (void), SD_COMMON_TYPES);
|
||||
|
||||
void ConstantHolder::addBuffer(ConstantDataBuffer& pointer, DataType dataType) { _buffers[dataType] = pointer; }
|
||||
|
||||
template <typename T>
|
||||
void ConstantHolder::addBuffer(ConstantDataBuffer& pointer) {
|
||||
addBuffer(pointer, DataTypeUtils::fromT<T>());
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void ConstantHolder::addBuffer, (ConstantDataBuffer & cb),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
ConstantDataBuffer* ConstantHolder::getConstantDataBuffer(DataType dataType) {
|
||||
if (!hasBuffer(dataType)) THROW_EXCEPTION("Requested dataType is absent in storage");
|
||||
|
||||
return &_buffers[dataType];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ConstantDataBuffer* ConstantHolder::getConstantDataBuffer() {
|
||||
return getConstantDataBuffer(DataTypeUtils::fromT<T>());
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT ConstantDataBuffer* ConstantHolder::getConstantDataBuffer, (),
|
||||
SD_COMMON_TYPES);
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/ConstantOffsetsBuffer.h>
|
||||
|
||||
namespace sd {
|
||||
ConstantOffsetsBuffer::ConstantOffsetsBuffer(const std::shared_ptr<PointerWrapper> &primary)
|
||||
: ConstantOffsetsBuffer(primary, std::shared_ptr<PointerWrapper>(nullptr)) {
|
||||
//
|
||||
}
|
||||
|
||||
ConstantOffsetsBuffer::ConstantOffsetsBuffer(const std::shared_ptr<PointerWrapper> &primary,
|
||||
const std::shared_ptr<PointerWrapper> &special) {
|
||||
_magic = MAGIC_VALID; // Mark as valid/constructed
|
||||
_primaryOffsets = primary;
|
||||
_specialOffsets = special;
|
||||
}
|
||||
|
||||
ConstantOffsetsBuffer::ConstantOffsetsBuffer() {
|
||||
_magic = MAGIC_VALID; // Mark default-constructed objects as valid
|
||||
// shared_ptr members are automatically initialized to nullptr
|
||||
}
|
||||
|
||||
ConstantOffsetsBuffer::~ConstantOffsetsBuffer() {
|
||||
_magic = 0xDEADBEEF; // Mark as destroyed - helps detect use-after-free
|
||||
}
|
||||
|
||||
LongType *ConstantOffsetsBuffer::primary() {
|
||||
// Check magic number first to detect use-after-free (dangling pointers)
|
||||
if (!isValid()) {
|
||||
THROW_EXCEPTION("ConstantOffsetsBuffer::primary: Object has been destroyed! Use-after-free detected (magic number check failed).");
|
||||
}
|
||||
if (_primaryOffsets == nullptr || !_primaryOffsets) {
|
||||
THROW_EXCEPTION("ConstantOffsetsBuffer::primary: _primaryOffsets is nullptr! Buffer was likely already destroyed.");
|
||||
}
|
||||
return reinterpret_cast<LongType *>(_primaryOffsets->pointer());
|
||||
}
|
||||
|
||||
LongType *ConstantOffsetsBuffer::special() {
|
||||
return _specialOffsets ? reinterpret_cast<LongType *>(_specialOffsets->pointer()) : nullptr;
|
||||
}
|
||||
|
||||
LongType *ConstantOffsetsBuffer::platform() {
|
||||
#ifdef SD_CUDA
|
||||
return special();
|
||||
#else
|
||||
return primary();
|
||||
#endif // CUDABLAS
|
||||
}
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/ConstantShapeBuffer.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace sd {
|
||||
ConstantShapeBuffer::ConstantShapeBuffer( PointerWrapper* primary)
|
||||
: ConstantShapeBuffer(primary, nullptr) {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
st = backward::StackTrace();
|
||||
st.load_here(32);
|
||||
#endif
|
||||
|
||||
}
|
||||
ConstantShapeBuffer::ConstantShapeBuffer() : _magic(MAGIC_VALID), _refCount(1) {
|
||||
_primaryShapeInfo = nullptr;
|
||||
_specialShapeInfo = nullptr;
|
||||
}
|
||||
ConstantShapeBuffer::~ConstantShapeBuffer() {
|
||||
// Clear magic number first to mark as invalid during destruction
|
||||
_magic = 0;
|
||||
|
||||
if(_primaryShapeInfo != nullptr)
|
||||
delete _primaryShapeInfo;
|
||||
_primaryShapeInfo = nullptr;
|
||||
|
||||
if(_specialShapeInfo != nullptr)
|
||||
delete _specialShapeInfo;
|
||||
_specialShapeInfo = nullptr;
|
||||
}
|
||||
|
||||
ConstantShapeBuffer::ConstantShapeBuffer( PointerWrapper* primary,
|
||||
PointerWrapper* special) : _magic(MAGIC_VALID), _refCount(1) {
|
||||
_primaryShapeInfo = primary;
|
||||
_specialShapeInfo = special;
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
st = backward::StackTrace();
|
||||
st.load_here(32);
|
||||
#endif
|
||||
}
|
||||
|
||||
LongType *ConstantShapeBuffer::primary() {
|
||||
if(_primaryShapeInfo != nullptr) {
|
||||
return reinterpret_cast<LongType *>(_primaryShapeInfo->pointer());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LongType *ConstantShapeBuffer::special() {
|
||||
if(_specialShapeInfo != nullptr) {
|
||||
return reinterpret_cast<LongType *>(_specialShapeInfo->pointer());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LongType *ConstantShapeBuffer::platform() {
|
||||
#ifdef SD_CUDA
|
||||
return special();
|
||||
#else
|
||||
return primary();
|
||||
#endif // CUDABLAS
|
||||
}
|
||||
|
||||
std::string ConstantShapeBuffer::getStackTraceAsString() const {
|
||||
#if defined(SD_GCC_FUNCTRACE) && !defined(__JAVACPP_HACK__)
|
||||
// Use backward::Printer to format the stack trace into a string
|
||||
std::ostringstream oss;
|
||||
backward::Printer p;
|
||||
p.snippet = false; // Don't include source code snippets
|
||||
p.address = true; // Include addresses
|
||||
p.object = false; // Don't include object file info
|
||||
p.color_mode = backward::ColorMode::never; // No ANSI colors in string
|
||||
|
||||
// Print to our string stream (we need to cast away const to use st)
|
||||
// This is safe since print doesn't modify the StackTrace
|
||||
backward::StackTrace& mutable_st = const_cast<backward::StackTrace&>(st);
|
||||
p.print(mutable_st, oss);
|
||||
|
||||
return oss.str();
|
||||
#else
|
||||
return ""; // Return empty string when functrace is not enabled
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConstantShapeBuffer::addRef() {
|
||||
_refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ConstantShapeBuffer::release() {
|
||||
// Decrement refcount - buffer stays in cache even when refcount reaches baseline
|
||||
// The cache owns these buffers and is responsible for their lifecycle
|
||||
// Don't delete when refcount reaches 1 - that means cache is the only owner
|
||||
_refCount.fetch_sub(1, std::memory_order_acq_rel);
|
||||
|
||||
// NOTE: Buffers are deleted only when the cache itself is cleared/destroyed,
|
||||
// not when temporary users release their references
|
||||
}
|
||||
|
||||
int ConstantShapeBuffer::getRefCount() const {
|
||||
return _refCount.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,739 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <array/DataBuffer.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <exceptions/allocation_exception.h>
|
||||
#include <execution/AffinityManager.h>
|
||||
#include <helpers/logger.h>
|
||||
#include <memory/MemoryCounter.h>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
#include <array/DataBufferLifecycleTracker.h>
|
||||
#endif
|
||||
|
||||
namespace sd {
|
||||
///// IMPLEMENTATION OF COMMON METHODS /////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// default constructor
|
||||
DataBuffer::DataBuffer() {
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer() default constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
_primaryBuffer = nullptr;
|
||||
_specialBuffer = nullptr;
|
||||
_lenInBytes = 0;
|
||||
_dataType = INT8;
|
||||
_workspace = nullptr;
|
||||
_isOwnerPrimary = false;
|
||||
_isOwnerSpecial = false;
|
||||
_deviceId = AffinityManager::currentDeviceId();
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
setCountersToZero();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// copy constructor
|
||||
DataBuffer::DataBuffer(const DataBuffer& other) {
|
||||
if(other._dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer(const DataBuffer& other) copy constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
_lenInBytes = other._lenInBytes;
|
||||
_dataType = other._dataType;
|
||||
_workspace = other._workspace;
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// Don't share stack traces - they will be created fresh when we allocate
|
||||
allocationStackTracePrimary = nullptr;
|
||||
allocationStackTraceSpecial = nullptr;
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
_primaryBuffer = other._primaryBuffer;
|
||||
_specialBuffer = other._specialBuffer;
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
|
||||
_deviceId.store(other._deviceId.load());
|
||||
|
||||
setCountersToZero();
|
||||
|
||||
allocateBuffers();
|
||||
copyBufferFrom(other);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DataBuffer::DataBuffer(void* primary, void* special, const size_t lenInBytes, const DataType dataType,
|
||||
const bool isOwnerPrimary, const bool isOwnerSpecial, memory::Workspace* workspace) {
|
||||
if(dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf(
|
||||
"DataBuffer::DataBuffer(void* primary, void* special, const size_t lenInBytes, const DataType dataType, const bool isOwnerPrimary, const bool isOwnerSpecial, memory::Workspace* workspace) constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
_primaryBuffer = primary;
|
||||
_specialBuffer = special;
|
||||
_lenInBytes = lenInBytes;
|
||||
_dataType = dataType;
|
||||
_workspace = workspace;
|
||||
_isOwnerPrimary = isOwnerPrimary;
|
||||
_isOwnerSpecial = isOwnerSpecial;
|
||||
_deviceId = AffinityManager::currentDeviceId();
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
setCountersToZero();
|
||||
|
||||
if (primary != nullptr) {
|
||||
readPrimary();
|
||||
}
|
||||
if (special != nullptr) {
|
||||
readSpecial();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DataBuffer::DataBuffer(void* primary, const size_t lenInBytes, const DataType dataType, const bool isOwnerPrimary,
|
||||
memory::Workspace* workspace)
|
||||
: DataBuffer(primary, nullptr, lenInBytes, dataType, isOwnerPrimary, false, workspace) {
|
||||
if(dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer(void* primary, const size_t lenInBytes, const DataType dataType, const bool isOwnerPrimary, memory::Workspace* workspace) constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if(primary != nullptr)
|
||||
syncToSpecial(true);
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// copies data from hostBuffer to own memory buffer
|
||||
DataBuffer::DataBuffer(const void* hostBuffer, const DataType dataType, const size_t lenInBytes,
|
||||
memory::Workspace* workspace) {
|
||||
if(dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer(const void* hostBuffer, const DataType dataType, const size_t lenInBytes, memory::Workspace* workspace) constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
if (hostBuffer == nullptr) {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
std::string traceInfo = getCreationTraceAsString();
|
||||
std::string errorMsg = "DataBuffer constructor: can't be initialized with nullptr host buffer !";
|
||||
if (!traceInfo.empty()) {
|
||||
errorMsg += "\n\nDataBuffer allocation trace:\n" + traceInfo;
|
||||
}
|
||||
THROW_EXCEPTION(errorMsg.c_str());
|
||||
#else
|
||||
THROW_EXCEPTION("DataBuffer constructor: can't be initialized with nullptr host buffer !");
|
||||
#endif
|
||||
}
|
||||
if (lenInBytes == 0) {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
std::string traceInfo = getCreationTraceAsString();
|
||||
std::string errorMsg = "DataBuffer constructor: can't be initialized with zero length !";
|
||||
if (!traceInfo.empty()) {
|
||||
errorMsg += "\n\nDataBuffer allocation trace:\n" + traceInfo;
|
||||
}
|
||||
THROW_EXCEPTION(errorMsg.c_str());
|
||||
#else
|
||||
THROW_EXCEPTION("DataBuffer constructor: can't be initialized with zero length !");
|
||||
#endif
|
||||
}
|
||||
|
||||
_primaryBuffer = nullptr;
|
||||
_specialBuffer = nullptr;
|
||||
_lenInBytes = lenInBytes;
|
||||
_dataType = dataType;
|
||||
_workspace = workspace;
|
||||
|
||||
_deviceId = AffinityManager::currentDeviceId();
|
||||
|
||||
setCountersToZero();
|
||||
|
||||
allocateBuffers();
|
||||
|
||||
copyBufferFromHost(hostBuffer, lenInBytes);
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DataBuffer::DataBuffer(const sd::LongType lenInBytes, const DataType dataType, memory::Workspace* workspace,
|
||||
const bool allocBoth) {
|
||||
|
||||
if(dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer(const size_t lenInBytes, const DataType dataType, memory::Workspace* workspace, const bool allocBoth) constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
_dataType = dataType;
|
||||
_workspace = workspace;
|
||||
_lenInBytes = lenInBytes;
|
||||
|
||||
_primaryBuffer = nullptr;
|
||||
_specialBuffer = nullptr;
|
||||
_isOwnerPrimary = false;
|
||||
_isOwnerSpecial = false;
|
||||
|
||||
_deviceId = AffinityManager::currentDeviceId();
|
||||
|
||||
setCountersToZero();
|
||||
|
||||
allocateBuffers(allocBoth);
|
||||
writeSpecial();
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// move constructor
|
||||
DataBuffer::DataBuffer(DataBuffer&& other) {
|
||||
|
||||
if(other._dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer constructor: dataType is UNKNOWN !");
|
||||
}
|
||||
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::DataBuffer(DataBuffer&& other) move constructor\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
_primaryBuffer = other._primaryBuffer;
|
||||
_specialBuffer = other._specialBuffer;
|
||||
_lenInBytes = other._lenInBytes;
|
||||
_dataType = other._dataType;
|
||||
_workspace = other._workspace;
|
||||
_isOwnerPrimary = other._isOwnerPrimary;
|
||||
_isOwnerSpecial = other._isOwnerSpecial;
|
||||
_deviceId.store(other._deviceId);
|
||||
|
||||
copyCounters(other);
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
allocationStackTracePrimary = other.allocationStackTracePrimary;
|
||||
allocationStackTraceSpecial = other.allocationStackTraceSpecial;
|
||||
creationStackTrace = other.creationStackTrace;
|
||||
// Transfer ownership - null out the source pointers to prevent double-free
|
||||
other.allocationStackTracePrimary = nullptr;
|
||||
other.allocationStackTraceSpecial = nullptr;
|
||||
other.creationStackTrace = nullptr;
|
||||
#endif
|
||||
other._primaryBuffer = other._specialBuffer = nullptr;
|
||||
other.setAllocFlags(false, false);
|
||||
other._lenInBytes = 0;
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// assignment operator
|
||||
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||
if(other._dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer assignment operator: dataType is UNKNOWN !");
|
||||
}
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::operator=(const DataBuffer& other) assignment operator\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
if (this == &other) return *this;
|
||||
|
||||
deleteBuffers();
|
||||
|
||||
_lenInBytes = other._lenInBytes;
|
||||
_dataType = other._dataType;
|
||||
_workspace = other._workspace;
|
||||
|
||||
allocateBuffers();
|
||||
copyBufferFrom(other);
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// move assignment operator
|
||||
DataBuffer& DataBuffer::operator=(DataBuffer&& other) noexcept {
|
||||
if(other._dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer move assignment operator: dataType is UNKNOWN !");
|
||||
}
|
||||
|
||||
if(Environment::getInstance().isLogNativeNDArrayCreation()) {
|
||||
printf("DataBuffer::operator=(DataBuffer&& other) move assignment operator\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
if (this == &other) return *this;
|
||||
|
||||
deleteBuffers();
|
||||
|
||||
_primaryBuffer = other._primaryBuffer;
|
||||
_specialBuffer = other._specialBuffer;
|
||||
_lenInBytes = other._lenInBytes;
|
||||
_dataType = other._dataType;
|
||||
_workspace = other._workspace;
|
||||
_isOwnerPrimary = other._isOwnerPrimary;
|
||||
_isOwnerSpecial = other._isOwnerSpecial;
|
||||
|
||||
copyCounters(other);
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
allocationStackTracePrimary = other.allocationStackTracePrimary;
|
||||
allocationStackTraceSpecial = other.allocationStackTraceSpecial;
|
||||
creationStackTrace = other.creationStackTrace;
|
||||
// Transfer ownership - null out the source pointers to prevent double-free
|
||||
other.allocationStackTracePrimary = nullptr;
|
||||
other.allocationStackTraceSpecial = nullptr;
|
||||
other.creationStackTrace = nullptr;
|
||||
#endif
|
||||
|
||||
other._primaryBuffer = other._specialBuffer = nullptr;
|
||||
other.setAllocFlags(false, false);
|
||||
other._lenInBytes = 0;
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - Stack trace capture via backward-cpp's backtrace() is NOT safe during early JVM initialization
|
||||
// - The JVM's memory mappings and signal handlers aren't fully set up yet
|
||||
// - This causes SIGSEGV crashes at addresses like 0x7f647edc2000 inside glibc internals
|
||||
// - Session #953's try-catch doesn't work when C++ exceptions are disabled (common for performance)
|
||||
// - DataBufferLifecycleTracker already captures stack traces separately for leak detection
|
||||
// - The creationStackTrace was redundant and only used for constructor error messages
|
||||
// - Solution: Leave creationStackTrace as nullptr (getCreationTraceAsString() handles this gracefully)
|
||||
// - This eliminates crashes while preserving all leak detection functionality
|
||||
creationStackTrace = nullptr;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void DataBuffer::markConstant(bool reallyConstant) {
|
||||
isConstant = reallyConstant;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Validation method following DirectShapeTrie pattern
|
||||
// Checks for use-after-free, corrupted pointers, and invalid state
|
||||
void DataBuffer::validateIntegrity() const {
|
||||
// Check magic number first - if wrong, pointer is dangling/corrupted
|
||||
if (_magicNumber != MAGIC_NUMBER) {
|
||||
// Magic number doesn't match - this is a freed/corrupted DataBuffer!
|
||||
std::stringstream ss;
|
||||
ss << "DataBuffer integrity check FAILED!\n";
|
||||
ss << " Expected magic number: 0x" << std::hex << MAGIC_NUMBER << "\n";
|
||||
ss << " Actual magic number: 0x" << std::hex << _magicNumber << "\n";
|
||||
ss << " Likely causes:\n";
|
||||
ss << " 1. Use-after-free: DataBuffer was deleted but pointer still used\n";
|
||||
ss << " 2. Corrupted pointer: Pointer points to invalid memory\n";
|
||||
ss << " 3. Uninitialized memory: DataBuffer was never properly constructed\n";
|
||||
ss << " This indicates a SERIOUS BUG in buffer lifecycle management!\n";
|
||||
ss << " Check where this DataBuffer pointer came from and ensure it's still valid.\n";
|
||||
THROW_EXCEPTION(ss.str().c_str());
|
||||
}
|
||||
|
||||
// Check if buffer has been closed
|
||||
if (closed) {
|
||||
std::stringstream ss;
|
||||
ss << "DataBuffer integrity check FAILED!\n";
|
||||
ss << " Buffer has been closed (freed) but is still being accessed\n";
|
||||
ss << " Magic number is valid (0x" << std::hex << _magicNumber << ") but closed flag is true\n";
|
||||
ss << " This indicates use-after-close: buffer was explicitly closed but pointer retained\n";
|
||||
THROW_EXCEPTION(ss.str().c_str());
|
||||
}
|
||||
|
||||
// Sanity check data type
|
||||
if (_dataType == DataType::UNKNOWN) {
|
||||
std::stringstream ss;
|
||||
ss << "DataBuffer integrity check FAILED!\n";
|
||||
ss << " DataType is UNKNOWN - buffer was not properly initialized\n";
|
||||
THROW_EXCEPTION(ss.str().c_str());
|
||||
}
|
||||
|
||||
// Sanity check length (negative or excessively large values indicate corruption)
|
||||
if (_lenInBytes < 0 || _lenInBytes > (1LL << 40)) { // 1TB limit
|
||||
std::stringstream ss;
|
||||
ss << "DataBuffer integrity check FAILED!\n";
|
||||
ss << " Length is invalid: " << _lenInBytes << " bytes\n";
|
||||
ss << " Valid range is 0 to " << (1LL << 40) << " bytes (1TB)\n";
|
||||
ss << " This indicates memory corruption\n";
|
||||
THROW_EXCEPTION(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void* DataBuffer::primary() {
|
||||
return _primaryBuffer;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void* DataBuffer::special() {
|
||||
return _specialBuffer;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DataType DataBuffer::getDataType() { return _dataType; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
size_t DataBuffer::getLenInBytes() const {
|
||||
// Check if buffer has been closed/freed
|
||||
if(closed) {
|
||||
return 0;
|
||||
}
|
||||
//we need minimum 1 for scalars
|
||||
if(_lenInBytes == 0) {
|
||||
if(_dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer getLenInBytes: dataType is UNKNOWN !");
|
||||
}
|
||||
return DataTypeUtils::sizeOfElement(_dataType);
|
||||
}
|
||||
return _lenInBytes;
|
||||
}
|
||||
size_t DataBuffer::getNumElements() {
|
||||
return _lenInBytes / DataTypeUtils::sizeOfElement(getDataType());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void DataBuffer::allocatePrimary() {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// DataBufferLifecycleTracker already captures allocations for leak detection
|
||||
if(allocationStackTracePrimary != nullptr) {
|
||||
delete allocationStackTracePrimary;
|
||||
allocationStackTracePrimary = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (_primaryBuffer == nullptr) {
|
||||
auto deviceId = AffinityManager::currentDeviceId();
|
||||
// check if this allocation won't bring us above limit
|
||||
if (_workspace == nullptr) {
|
||||
if (Environment::getInstance().isCPU()) {
|
||||
// on cpu backend we validate against device 0 for now
|
||||
if (!memory::MemoryCounter::getInstance().validate(getLenInBytes()))
|
||||
THROW_EXCEPTION(allocation_exception::build("Requested amount exceeds HOST device limits",
|
||||
memory::MemoryCounter::getInstance().deviceLimit(deviceId),
|
||||
getLenInBytes()).what());
|
||||
} else {
|
||||
// in heterogenuous mode we validate against device group
|
||||
if (!memory::MemoryCounter::getInstance().validateGroup(memory::MemoryType::HOST, getLenInBytes()))
|
||||
THROW_EXCEPTION(allocation_exception::build(
|
||||
"Requested amount exceeds HOST group limits",
|
||||
memory::MemoryCounter::getInstance().groupLimit(memory::MemoryType::HOST), getLenInBytes()).what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ALLOCATE(_primaryBuffer, _workspace, getLenInBytes(), int8_t);
|
||||
_isOwnerPrimary = true;
|
||||
|
||||
// count in towards current deviceId if we're not in workspace mode
|
||||
if (_workspace == nullptr) {
|
||||
if (Environment::getInstance().isCPU()) // we don't want this counter to be added to CUDA device
|
||||
memory::MemoryCounter::getInstance().countIn(deviceId, getLenInBytes());
|
||||
|
||||
memory::MemoryCounter::getInstance().countIn(memory::MemoryType::HOST, getLenInBytes());
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// Record allocation in lifecycle tracker
|
||||
array::DataBufferLifecycleTracker::getInstance().recordAllocation(
|
||||
_primaryBuffer, getLenInBytes(), getDataType(),
|
||||
array::BufferType::PRIMARY, this, _workspace != nullptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void DataBuffer::setAllocFlags(const bool isOwnerPrimary, const bool isOwnerSpecial) {
|
||||
_isOwnerPrimary = isOwnerPrimary;
|
||||
_isOwnerSpecial = isOwnerSpecial;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void DataBuffer::deletePrimary() {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
printPrimaryAllocationStackTraces();
|
||||
|
||||
#endif
|
||||
if (_isOwnerPrimary && _primaryBuffer != nullptr) {
|
||||
auto p = reinterpret_cast<int8_t*>(_primaryBuffer);
|
||||
|
||||
if(Environment::getInstance().isDeletePrimary()) {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// Record deallocation before releasing memory
|
||||
array::DataBufferLifecycleTracker::getInstance().recordDeallocation(
|
||||
_primaryBuffer, array::BufferType::PRIMARY);
|
||||
#endif
|
||||
RELEASE(p, _workspace);
|
||||
_primaryBuffer = nullptr;
|
||||
}
|
||||
|
||||
_isOwnerPrimary = false;
|
||||
|
||||
// count out towards DataBuffer device, only if we're not in workspace
|
||||
if (_workspace == nullptr) {
|
||||
if (Environment::getInstance().isCPU()) memory::MemoryCounter::getInstance().countOut(_deviceId, getLenInBytes());
|
||||
|
||||
memory::MemoryCounter::getInstance().countOut(memory::MemoryType::HOST, getLenInBytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void DataBuffer::printPrimaryAllocationStackTraces() {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void DataBuffer::deleteBuffers() {
|
||||
if(isConstant || closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_deleteMutex);
|
||||
deletePrimary();
|
||||
deleteSpecial();
|
||||
|
||||
// Clean up stack traces to prevent memory leak
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
if(allocationStackTracePrimary != nullptr) {
|
||||
delete allocationStackTracePrimary;
|
||||
allocationStackTracePrimary = nullptr;
|
||||
}
|
||||
if(allocationStackTraceSpecial != nullptr) {
|
||||
delete allocationStackTraceSpecial;
|
||||
allocationStackTraceSpecial = nullptr;
|
||||
}
|
||||
if(creationStackTrace != nullptr) {
|
||||
delete creationStackTrace;
|
||||
creationStackTrace = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
closed = true;
|
||||
_lenInBytes = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DataBuffer::~DataBuffer() {
|
||||
// Clear magic number to detect use-after-free
|
||||
// If anyone tries to use this buffer after destruction, validateIntegrity() will catch it
|
||||
_magicNumber = 0xDEADBEEF;
|
||||
deleteBuffers();
|
||||
}
|
||||
|
||||
|
||||
void DataBuffer::setPrimaryBuffer(void* buffer, size_t length) {
|
||||
std::lock_guard<std::mutex> lock(_deleteMutex);
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// DataBufferLifecycleTracker already captures allocations for leak detection
|
||||
if(allocationStackTracePrimary != nullptr) {
|
||||
delete allocationStackTracePrimary;
|
||||
allocationStackTracePrimary = nullptr;
|
||||
}
|
||||
#endif
|
||||
_primaryBuffer = buffer;
|
||||
_isOwnerPrimary = false;
|
||||
_lenInBytes = length * DataTypeUtils::sizeOf(_dataType);
|
||||
}
|
||||
|
||||
void DataBuffer::setSpecialBuffer(void* buffer, size_t length) {
|
||||
std::lock_guard<std::mutex> lock(_deleteMutex);
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// DataBufferLifecycleTracker already captures allocations for leak detection
|
||||
if(allocationStackTraceSpecial != nullptr) {
|
||||
delete allocationStackTraceSpecial;
|
||||
allocationStackTraceSpecial = nullptr;
|
||||
}
|
||||
#endif
|
||||
this->setSpecial(buffer, false);
|
||||
_lenInBytes = length * DataTypeUtils::sizeOf(_dataType);
|
||||
}
|
||||
|
||||
void DataBuffer::setDataType(DataType dataType) {
|
||||
if(dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("DataBuffer setDataType: dataType is UNKNOWN !");
|
||||
}
|
||||
_dataType = dataType;
|
||||
}
|
||||
|
||||
void DataBuffer::printAllocationTrace() {
|
||||
if(closed) {
|
||||
printf("DataBuffer::printAllocationTrace() - buffer is closed\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
//print whether each stack trace is null or not:
|
||||
Printer p;
|
||||
if(allocationStackTracePrimary != nullptr) {
|
||||
p.print(*allocationStackTracePrimary);
|
||||
}
|
||||
if(allocationStackTraceSpecial != nullptr) {
|
||||
p.print(*allocationStackTraceSpecial);
|
||||
}
|
||||
if(creationStackTrace != nullptr) {
|
||||
p.print(*creationStackTrace);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string DataBuffer::getCreationTraceAsString() const {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
if (creationStackTrace == nullptr || creationStackTrace->size() == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
backward::TraceResolver resolver;
|
||||
resolver.load_stacktrace(*creationStackTrace);
|
||||
|
||||
for (size_t i = 0; i < creationStackTrace->size(); ++i) {
|
||||
const backward::ResolvedTrace &trace = resolver.resolve((*creationStackTrace)[i]);
|
||||
|
||||
// Format: #frame function_name at source_file:line
|
||||
oss << "#" << i << " ";
|
||||
|
||||
if (!trace.object_function.empty()) {
|
||||
oss << trace.object_function;
|
||||
} else {
|
||||
oss << "???";
|
||||
}
|
||||
|
||||
if (!trace.source.filename.empty()) {
|
||||
oss << " at " << trace.source.filename;
|
||||
if (trace.source.line > 0) {
|
||||
oss << ":" << trace.source.line;
|
||||
}
|
||||
}
|
||||
|
||||
oss << "\n";
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
int DataBuffer::deviceId() const { return _deviceId.load(); }
|
||||
|
||||
void DataBuffer::close() { this->deleteBuffers(); }
|
||||
|
||||
void DataBuffer::setDeviceId(int deviceId) { _deviceId = deviceId; }
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,39 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/DataType.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <types/float16.h>
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
namespace sd {
|
||||
DataType DataTypeUtils::fromInt(int val) { return (DataType)val; }
|
||||
|
||||
DataType DataTypeUtils::fromFlatDataType(graph::DType dtype) { return (DataType)dtype; }
|
||||
|
||||
int DataTypeUtils::asInt(DataType type) { return static_cast<int>(type); }
|
||||
|
||||
/**
|
||||
* Check if a triple of data types is enabled for compilation in selective rendering
|
||||
*/
|
||||
static bool isCompiledTypeTriple(DataType type1, DataType type2, DataType type3);
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,130 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/DataType.h>
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <array/ExtraArguments.h>
|
||||
#include <types/types.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef SD_CUDA
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime.h>
|
||||
#endif
|
||||
|
||||
namespace sd {
|
||||
ExtraArguments::ExtraArguments(std::initializer_list<double> arguments) { _fpArgs = arguments; }
|
||||
|
||||
ExtraArguments::ExtraArguments(std::initializer_list<LongType> arguments) { _intArgs = arguments; }
|
||||
|
||||
ExtraArguments::ExtraArguments(const std::vector<double> &arguments) { _fpArgs = arguments; }
|
||||
|
||||
ExtraArguments::ExtraArguments(const std::vector<LongType> &arguments) { _intArgs = arguments; }
|
||||
|
||||
ExtraArguments::ExtraArguments(const std::vector<int> &arguments) {
|
||||
for (const auto &v : arguments) _intArgs.emplace_back(static_cast<LongType>(v));
|
||||
}
|
||||
|
||||
ExtraArguments::ExtraArguments() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
ExtraArguments::~ExtraArguments() {
|
||||
for (auto p : _pointers) {
|
||||
#ifdef SD_CUDA
|
||||
cudaFree(p);
|
||||
#else // CPU branch
|
||||
delete reinterpret_cast<int8_t *>(p);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ExtraArguments::convertAndCopy(Pointer pointer, LongType offset) {
|
||||
auto length = this->length();
|
||||
auto target = reinterpret_cast<T *>(pointer);
|
||||
#ifdef SD_CUDA
|
||||
target = new T[length];
|
||||
#endif
|
||||
|
||||
if (!_fpArgs.empty()) {
|
||||
for (size_t e = offset; e < _fpArgs.size(); e++) {
|
||||
target[e] = static_cast<T>(_fpArgs[e]);
|
||||
}
|
||||
} else if (_intArgs.empty()) {
|
||||
for (size_t e = offset; e < _intArgs.size(); e++) {
|
||||
target[e] = static_cast<T>(_intArgs[e]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SD_CUDA
|
||||
cudaMemcpy(pointer, target, length * DataTypeUtils::sizeOf(DataTypeUtils::fromT<T>()), cudaMemcpyHostToDevice);
|
||||
delete[] target;
|
||||
#endif
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void ExtraArguments::convertAndCopy,
|
||||
(sd::Pointer pointer, sd::LongType offset), SD_COMMON_TYPES);
|
||||
|
||||
void *ExtraArguments::allocate(size_t length, size_t elementSize) {
|
||||
#ifdef SD_CUDA
|
||||
Pointer ptr;
|
||||
auto res = cudaMalloc(reinterpret_cast<void **>(&ptr), length * elementSize);
|
||||
if (res != 0) THROW_EXCEPTION("Can't allocate CUDA memory");
|
||||
#else // CPU branch
|
||||
auto ptr = new int8_t[length * elementSize];
|
||||
if (!ptr) THROW_EXCEPTION("Can't allocate memory");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
size_t ExtraArguments::length() {
|
||||
if (!_fpArgs.empty())
|
||||
return _fpArgs.size();
|
||||
else if (!_intArgs.empty())
|
||||
return _intArgs.size();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void *ExtraArguments::argumentsAsT(LongType offset) {
|
||||
return argumentsAsT(DataTypeUtils::fromT<T>(), offset);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void *ExtraArguments::argumentsAsT, (sd::LongType offset),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
void *ExtraArguments::argumentsAsT(DataType dataType, LongType offset) {
|
||||
if (_fpArgs.empty() && _intArgs.empty()) return nullptr;
|
||||
|
||||
// we allocate pointer
|
||||
auto ptr = allocate(length() - offset, DataTypeUtils::sizeOf(dataType));
|
||||
|
||||
// fill it with data
|
||||
BUILD_SINGLE_SELECTOR(dataType, convertAndCopy, (ptr, offset), SD_COMMON_TYPES);
|
||||
|
||||
// store it internally for future release
|
||||
_pointers.emplace_back(ptr);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,231 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <array/InteropDataBuffer.h>
|
||||
#include <execution/AffinityManager.h>
|
||||
#include <helpers/logger.h>
|
||||
|
||||
namespace sd {
|
||||
InteropDataBuffer::InteropDataBuffer(InteropDataBuffer* dataBuffer, uint64_t length) {
|
||||
if(dataBuffer == nullptr) {
|
||||
THROW_EXCEPTION("InteropDataBuffer::InteropDataBuffer(InteropDataBuffer& dataBuffer, uint64_t length, uint64_t offset) - dataBuffer is nullptr");
|
||||
}
|
||||
if(dataBuffer->_dataBuffer->getDataType() == DataType::UNKNOWN)
|
||||
THROW_EXCEPTION("InteropDataBuffer::InteropDataBuffer(InteropDataBuffer& dataBuffer, uint64_t length, uint64_t offset) - dataBuffer has unknown data type");
|
||||
_dataBuffer = dataBuffer->dataBuffer();
|
||||
_dataType = dataBuffer->_dataType;
|
||||
|
||||
_cachedLenInBytes = length * DataTypeUtils::sizeOf(_dataType);
|
||||
// Cache pointers for deallocation tracking
|
||||
if (_dataBuffer != nullptr) {
|
||||
_cachedPrimaryPtr = _dataBuffer->primary();
|
||||
_cachedSpecialPtr = _dataBuffer->special();
|
||||
}
|
||||
|
||||
owner = false;
|
||||
}
|
||||
|
||||
InteropDataBuffer::InteropDataBuffer(DataBuffer * databuffer) {
|
||||
_dataBuffer = databuffer;
|
||||
_dataType = databuffer->getDataType();
|
||||
if(_dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION(
|
||||
"InteropDataBuffer::InteropDataBuffer(size_t lenInBytes, DataType dtype, bool allocateBoth) - data type is unknown");
|
||||
}
|
||||
// Cache the size to avoid accessing freed memory later
|
||||
_cachedLenInBytes = databuffer != nullptr ? databuffer->getLenInBytes() : 0;
|
||||
// Cache pointers for deallocation tracking
|
||||
if (databuffer != nullptr) {
|
||||
_cachedPrimaryPtr = databuffer->primary();
|
||||
_cachedSpecialPtr = databuffer->special();
|
||||
}
|
||||
// When wrapping an existing DataBuffer, we don't own it by default
|
||||
owner = false;
|
||||
}
|
||||
|
||||
InteropDataBuffer::InteropDataBuffer(size_t lenInBytes, DataType dtype, bool allocateBoth) {
|
||||
if(dtype == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION(
|
||||
"InteropDataBuffer::InteropDataBuffer(size_t lenInBytes, DataType dtype, bool allocateBoth) - data type is unknown");
|
||||
}
|
||||
|
||||
_cachedLenInBytes = lenInBytes;
|
||||
|
||||
if (lenInBytes == 0) {
|
||||
_dataBuffer = nullptr;
|
||||
this->_dataType = dtype;
|
||||
|
||||
} else {
|
||||
//note this should be size in bytes hence why we multiply the number of elements by the size of the data type
|
||||
_dataBuffer = new DataBuffer(lenInBytes, dtype, nullptr, allocateBoth);
|
||||
this->_dataType = dtype;
|
||||
this->markOwner(true);
|
||||
// Cache pointers for deallocation tracking
|
||||
_cachedPrimaryPtr = _dataBuffer->primary();
|
||||
_cachedSpecialPtr = _dataBuffer->special();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InteropDataBuffer::printDbAllocationTrace() {
|
||||
if(_dataBuffer == nullptr)
|
||||
return;
|
||||
_dataBuffer->printAllocationTrace();
|
||||
}
|
||||
|
||||
void InteropDataBuffer::markOwner(bool owner) {
|
||||
this->owner = owner;
|
||||
if(_dataBuffer != nullptr && !_closed) {
|
||||
this->_dataBuffer->_isOwnerPrimary = owner;
|
||||
this->_dataBuffer->_isOwnerSpecial = owner;
|
||||
}
|
||||
}
|
||||
|
||||
DataBuffer * InteropDataBuffer::getDataBuffer() const {
|
||||
//this can effect size of calculations among others
|
||||
if(_dataType == DataType::UNKNOWN) {
|
||||
THROW_EXCEPTION("All interop buffers must have a known data type.");
|
||||
}
|
||||
// Don't access _dataBuffer if it's been closed/freed
|
||||
if(_dataBuffer != nullptr && !_closed && _dataBuffer->_dataType == DataType::UNKNOWN) {
|
||||
_dataBuffer->_dataType = _dataType;
|
||||
}
|
||||
// Return nullptr if closed to prevent use-after-free
|
||||
return _closed ? nullptr : _dataBuffer;
|
||||
}
|
||||
|
||||
DataBuffer * InteropDataBuffer::dataBuffer() {
|
||||
if(_dataBuffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return _dataBuffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* InteropDataBuffer::primary() const {
|
||||
if(_dataBuffer == nullptr || _dataBuffer->primary() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<int8_t*>(_dataBuffer->primary());
|
||||
}
|
||||
|
||||
void* InteropDataBuffer::special() const {
|
||||
if(_dataBuffer == nullptr)
|
||||
return nullptr;
|
||||
if(_dataBuffer->special() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<int8_t*>(_dataBuffer->special());
|
||||
}
|
||||
|
||||
void InteropDataBuffer::setSpecial(void* ptr, size_t length) {
|
||||
if(_dataBuffer == nullptr)
|
||||
THROW_EXCEPTION("InteropDataBuffer::setSpecial() - _dataBuffer is nullptr");
|
||||
if(_closed)
|
||||
return; // Silently ignore if buffer was already closed
|
||||
_dataBuffer->setSpecialBuffer(ptr, length);
|
||||
// Update cached pointer
|
||||
_cachedSpecialPtr = ptr;
|
||||
}
|
||||
|
||||
void InteropDataBuffer::setPrimary(void* ptr, size_t length) {
|
||||
if(_dataBuffer == nullptr)
|
||||
THROW_EXCEPTION("InteropDataBuffer::setPrimary() - _dataBuffer is nullptr");
|
||||
if(_closed)
|
||||
return; // Silently ignore if buffer was already closed
|
||||
_dataBuffer->setPrimaryBuffer(ptr, length);
|
||||
// Update cached pointer
|
||||
_cachedPrimaryPtr = ptr;
|
||||
}
|
||||
|
||||
void InteropDataBuffer::setDeviceId(int deviceId) {
|
||||
if(_dataBuffer == nullptr || _closed)
|
||||
return;
|
||||
_dataBuffer->setDeviceId(deviceId);
|
||||
}
|
||||
int InteropDataBuffer::deviceId() const {
|
||||
if(_dataBuffer == nullptr || _closed)
|
||||
return 0;
|
||||
return _dataBuffer->deviceId();
|
||||
}
|
||||
|
||||
int InteropDataBuffer::useCount() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void InteropDataBuffer::registerSpecialUse(const std::vector<const InteropDataBuffer*>& writeList,
|
||||
const std::vector<const InteropDataBuffer*>& readList) {
|
||||
for (const auto& v : writeList) {
|
||||
if (v == nullptr) continue;
|
||||
|
||||
v->getDataBuffer()->writeSpecial();
|
||||
}
|
||||
}
|
||||
|
||||
void InteropDataBuffer::prepareSpecialUse(const std::vector<const InteropDataBuffer*>& writeList,
|
||||
const std::vector<const InteropDataBuffer*>& readList,
|
||||
bool synchronizeWritables) {
|
||||
auto currentDeviceId = AffinityManager::currentDeviceId();
|
||||
for (const auto& v : readList) {
|
||||
if (v == nullptr || v->_closed) continue;
|
||||
|
||||
auto db = v->getDataBuffer();
|
||||
if(db == nullptr) continue;
|
||||
|
||||
if (db->deviceId() != currentDeviceId) db->migrate();
|
||||
db->syncToSpecial();
|
||||
}
|
||||
|
||||
// we don't tick write list, only ensure the same device affinity
|
||||
for (const auto& v : writeList) {
|
||||
if (v == nullptr || v->_closed) continue;
|
||||
|
||||
auto db = v->getDataBuffer();
|
||||
if(db == nullptr) continue;
|
||||
|
||||
// special case for legacy ops - views can be updated on host side, thus original array can be not updated
|
||||
if (!db->isSpecialActual()) db->syncToSpecial();
|
||||
|
||||
if (db->deviceId() != currentDeviceId) db->migrate();
|
||||
}
|
||||
}
|
||||
|
||||
void InteropDataBuffer::registerPrimaryUse(const std::vector<const InteropDataBuffer*>& writeList,
|
||||
const std::vector<const InteropDataBuffer*>& readList) {
|
||||
|
||||
}
|
||||
|
||||
void InteropDataBuffer::preparePrimaryUse(const std::vector<const InteropDataBuffer*>& writeList,
|
||||
const std::vector<const InteropDataBuffer*>& readList,
|
||||
bool synchronizeWritables) {
|
||||
|
||||
}
|
||||
|
||||
void InteropDataBuffer::expand(size_t newlength) {
|
||||
if(_dataBuffer == nullptr || _closed)
|
||||
return; // Cannot expand a closed or null buffer
|
||||
_dataBuffer->expand(newlength * DataTypeUtils::sizeOf(_dataBuffer->getDataType()));
|
||||
}
|
||||
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by GS <sgazeos@gmail.com> on 2018-12-20.
|
||||
// @author Oleg Semeniv <oleg.semeniv@gmail.com>
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <graph/GraphExecutioner.h>
|
||||
#include <helpers/ConstantHelper.h>
|
||||
#include <helpers/ConstantShapeHelper.h>
|
||||
#include <helpers/LoopsCoordsHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <helpers/StringUtils.h>
|
||||
#include <legacy/NativeOps.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace sd {
|
||||
|
||||
SD_LIB_EXPORT NDArray* NDArrayFactory::create(ShapeDescriptor *shapeDescriptor, LaunchContext* context) {
|
||||
auto status = shapeDescriptor->validate();
|
||||
if (status != SHAPE_DESC_OK) {
|
||||
THROW_EXCEPTION("NDArrayFactory::create: invalid ShapeDescriptor ");
|
||||
}
|
||||
LongType allocSize = shapeDescriptor->allocLength() * DataTypeUtils::sizeOfElement(shapeDescriptor->dataType());
|
||||
DataBuffer * buffer =
|
||||
new DataBuffer(allocSize, shapeDescriptor->dataType(), context->getWorkspace());
|
||||
NDArray *result = new NDArray(buffer, shapeDescriptor, context);
|
||||
result->nullify();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
SD_LIB_EXPORT NDArray* NDArrayFactory::create<bool>(const char order, const std::vector<LongType>& shape,
|
||||
const std::vector<bool>& data, LaunchContext* context) {
|
||||
if ((int)shape.size() > SD_MAX_RANK)
|
||||
THROW_EXCEPTION("NDArrayFactory::create: rank of NDArray can't exceed 32 !");
|
||||
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor(BOOL, order, shape);
|
||||
|
||||
if (static_cast<size_t>(descriptor->arrLength()) != data.size()) {
|
||||
sd_printf("NDArrayFactory::create: data size [%i] doesn't match shape length [%lld]\n", data.size(),
|
||||
descriptor->arrLength());
|
||||
THROW_EXCEPTION("NDArrayFactory::create: data size doesn't match shape");
|
||||
}
|
||||
|
||||
bool* hostBuffer = nullptr;
|
||||
ALLOCATE(hostBuffer, context->getWorkspace(), data.size(), bool);
|
||||
std::copy(data.begin(), data.end(), hostBuffer);
|
||||
|
||||
DataBuffer * buffer = new DataBuffer(hostBuffer, data.size() * sizeof(bool), BOOL, true, context->getWorkspace());
|
||||
|
||||
NDArray *result = new NDArray(buffer, descriptor, context);
|
||||
delete descriptor;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create(const char order,
|
||||
const std::vector<LongType>& shape,
|
||||
const std::vector<T>& data,
|
||||
LaunchContext* context) {
|
||||
if (shape.size() > SD_MAX_RANK)
|
||||
THROW_EXCEPTION("NDArrayFactory::create: rank of NDArray can't exceed 32 !");
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor(DataTypeUtils::fromT<T>(), order, shape);
|
||||
|
||||
//scalars can be created with zero length
|
||||
if (descriptor->arrLength() != 0 && data.size() != 1 && static_cast<size_t>(descriptor->arrLength()) != data.size()) {
|
||||
delete descriptor;
|
||||
sd_printf("NDArrayFactory::create: data size [%i] doesn't match shape length [%lld]\n", data.size(),
|
||||
descriptor->arrLength());
|
||||
THROW_EXCEPTION("NDArrayFactory::create: data size doesn't match shape");
|
||||
}
|
||||
|
||||
T *hostData = nullptr;
|
||||
ALLOCATE(hostData, context->getWorkspace(), data.size(), T);
|
||||
std::copy(data.begin(), data.end(), hostData);
|
||||
|
||||
//note here we use data.size() to work around the scalar case. If the shape is zero but the data is actually length 1 we need this reflected
|
||||
//to create a correct length data buffer
|
||||
auto dtypeString = DataTypeUtils::asString(descriptor->dataType());
|
||||
DataBuffer * buffer = new DataBuffer(
|
||||
hostData, DataTypeUtils::fromT<T>(), data.size() * sizeof(T), context->getWorkspace());
|
||||
|
||||
NDArray *result = new NDArray(buffer, descriptor, context);
|
||||
delete descriptor;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update the instantiation macro to use the expanded type pattern
|
||||
#define TMPL_INSTANTIATE_CREATE_A_TYPE(TYPE) \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create<TYPE>(const char order, const std::vector<sd::LongType>& shape, \
|
||||
const std::vector<TYPE>& data, sd::LaunchContext* context);
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_A(T) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(T), \
|
||||
CONCAT(EXPAND_TYPE_APPLY_, GET_SECOND(T))(TMPL_INSTANTIATE_CREATE_A_TYPE) \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_NUMERIC_TYPES), TMPL_INSTANTIATE_CREATE_A)
|
||||
|
||||
#undef TMPL_INSTANTIATE_CREATE_A_TYPE
|
||||
#undef TMPL_INSTANTIATE_CREATE_A
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create_(const char order, std::vector<LongType>& shape, LaunchContext* context) {
|
||||
return create_(order, shape, DataTypeUtils::fromT<T>(), context);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE(NDArray* NDArrayFactory::create_,
|
||||
(const char order, std::vector<sd::LongType>& shape, sd::LaunchContext* context),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void NDArrayFactory::memcpyFromVector(void* ptr, const std::vector<T>& vector) {
|
||||
memcpy(ptr, vector.data(), vector.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <>
|
||||
void SD_LIB_EXPORT NDArrayFactory::memcpyFromVector(void* ptr, const std::vector<bool>& vector) {
|
||||
auto p = reinterpret_cast<bool*>(ptr);
|
||||
for (size_t e = 0; e < vector.size(); e++) p[e] = vector[e];
|
||||
}
|
||||
|
||||
|
||||
#define TMPL_INSTANTIATE_MEMCPY(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT void NDArrayFactory::memcpyFromVector<GET_SECOND(TYPE)>(void* ptr, const std::vector<GET_SECOND(TYPE)>& vector); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_NUMERIC_TYPES), TMPL_INSTANTIATE_MEMCPY)
|
||||
#undef TMPL_INSTANTIATE_MEMCPY
|
||||
|
||||
#ifndef __JAVACPP_HACK__
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::valueOf(const std::initializer_list<LongType>& shape, const T value, const char order,
|
||||
LaunchContext* context) {
|
||||
std::vector<sd::LongType> shape2 = shape;
|
||||
return valueOf(shape2, value, order);
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_VALUEOF_A(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::valueOf<GET_SECOND(TYPE)>(const std::initializer_list<sd::LongType>& shape, \
|
||||
const GET_SECOND(TYPE) value, const char order, \
|
||||
sd::LaunchContext* context); \
|
||||
))
|
||||
ITERATE_LIST((SD_NUMERIC_TYPES), TMPL_INSTANTIATE_VALUEOF_A)
|
||||
|
||||
#undef TMPL_INSTANTIATE_VALUEOF_A
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create_(const T scalar, LaunchContext* context) {
|
||||
sd::LongType size = DataTypeUtils::sizeOfElement(DataTypeUtils::fromT<T>());
|
||||
DataBuffer * buffer =
|
||||
new DataBuffer(size,
|
||||
DataTypeUtils::fromT<T>(),
|
||||
context->getWorkspace(),
|
||||
true);
|
||||
|
||||
auto desc = ShapeBuilders::createScalarShapeInfo(DataTypeUtils::fromT<T>());
|
||||
auto constDesc = ConstantShapeHelper::getInstance().bufferForShapeInfo(desc);
|
||||
auto recast = const_cast<LongType*>(constDesc->primary());
|
||||
NDArray* res = new NDArray(buffer, recast, context);
|
||||
res->p<T>(0,scalar);
|
||||
|
||||
res->tickWriteHost();
|
||||
res->syncToDevice();
|
||||
|
||||
delete[] desc; // Free allocated shape info
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_C(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create_<GET_SECOND(TYPE)>(const GET_SECOND(TYPE) scalar, sd::LaunchContext* context); \
|
||||
))
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_CREATE_C)
|
||||
|
||||
#undef TMPL_INSTANTIATE_CREATE_C
|
||||
|
||||
NDArray* NDArrayFactory::create(DataType dtype, LaunchContext *context) {
|
||||
return create(dtype,0, context);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create(DataType type, const T scalar, LaunchContext* context) {
|
||||
if (type == DataTypeUtils::fromT<T>()) return NDArrayFactory::create(scalar, context);
|
||||
|
||||
NDArray *res = new NDArray(type, context);
|
||||
res->p(0, scalar);
|
||||
res->syncToDevice();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_D(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create<GET_SECOND(TYPE)>(DataType type, const GET_SECOND(TYPE) scalar, sd::LaunchContext* context); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_CREATE_D)
|
||||
|
||||
#undef TMPL_INSTANTIATE_CREATE_D
|
||||
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create(const T scalar, LaunchContext* context) {
|
||||
DataBuffer * buffer =
|
||||
new DataBuffer(1 * sizeof(T), DataTypeUtils::fromT<T>(), context->getWorkspace(), true);
|
||||
|
||||
auto desc = ShapeDescriptor::scalarDescriptor(DataTypeUtils::fromT<T>());
|
||||
NDArray *res = new NDArray(buffer,desc , context);
|
||||
res->bufferAsT<T>()[0] = scalar;
|
||||
|
||||
res->tickWriteHost();
|
||||
res->syncToDevice();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_E(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create<GET_SECOND(TYPE)>(const GET_SECOND(TYPE) scalar, sd::LaunchContext* context); \
|
||||
))
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_CREATE_E)
|
||||
#undef TMPL_INSTANTIATE_CREATE_E
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::create_(const char order, const std::vector<LongType>& shape, const std::vector<T>& data,
|
||||
LaunchContext* context) {
|
||||
return NDArrayFactory::create<T>(order, shape, data, context);
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_F(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create_<GET_SECOND(TYPE)>(const char order, const std::vector<sd::LongType>& shape, \
|
||||
const std::vector<GET_SECOND(TYPE)>& data, sd::LaunchContext* context); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_CREATE_F)
|
||||
|
||||
|
||||
#undef TMPL_INSTANTIATE_CREATE_F
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
SD_LIB_EXPORT NDArray* NDArrayFactory::valueOf(std::vector<LongType>& shape, NDArray* value, const char order,
|
||||
LaunchContext* context) {
|
||||
auto result = create_(order, shape, value->dataType(), context);
|
||||
result->assign(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
SD_LIB_EXPORT NDArray* NDArrayFactory::valueOf(std::vector<LongType>& shape, NDArray& value, const char order,
|
||||
LaunchContext* context) {
|
||||
auto result = create_(order, shape, value.dataType(), context);
|
||||
result->assign(&value);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::valueOf(std::vector<LongType>& shape, T value, const char order,
|
||||
LaunchContext* context) {
|
||||
auto result = create_(order, shape, DataTypeUtils::fromT<T>());
|
||||
result->assign(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replace TMPL_INSTANTIATE_VALUEOF
|
||||
#define TMPL_INSTANTIATE_VALUEOF(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* \
|
||||
NDArrayFactory::valueOf<GET_SECOND(TYPE)>(std::vector<sd::LongType>& shape, GET_SECOND(TYPE) value, \
|
||||
const char order, sd::LaunchContext* context); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_VALUEOF)
|
||||
|
||||
|
||||
#undef TMPL_INSTANTIATE_VALUEOF
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::linspace(const T from, const T to, const LongType numElements) {
|
||||
NDArray* result = NDArrayFactory::vector<T>(numElements);
|
||||
// TO DO: linspace should be executed on DEVICE, but only CPU version implemnted!
|
||||
for (LongType e = 0; e < numElements; e++) {
|
||||
T step = (T)e / ((T)numElements - (T)1);
|
||||
result->p<T>(e, (from * ((T)1 - step) + step * to));
|
||||
}
|
||||
result->syncToDevice();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_LINSPACE(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::linspace<GET_SECOND(TYPE)>(const GET_SECOND(TYPE) from, const GET_SECOND(TYPE) to, \
|
||||
const sd::LongType numElements); \
|
||||
))
|
||||
ITERATE_LIST((SD_NUMERIC_TYPES), TMPL_INSTANTIATE_LINSPACE)
|
||||
|
||||
|
||||
|
||||
#undef TMPL_INSTANTIATE_LINSPACE
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::vector(LongType length, T value, LaunchContext* context) {
|
||||
DataBuffer * buffer =
|
||||
new DataBuffer(length * sizeof(T), DataTypeUtils::fromT<T>(), context->getWorkspace(), true);
|
||||
auto desc = ShapeBuilders::createVectorShapeInfo(DataTypeUtils::fromT<T>(),length);
|
||||
auto constDesc = ConstantShapeHelper::getInstance().bufferForShapeInfo(desc);
|
||||
auto recast = const_cast<LongType*>(constDesc->primary());
|
||||
auto res = new NDArray(buffer, recast, context);
|
||||
if (value == (T)0.0f)
|
||||
res->nullify();
|
||||
else
|
||||
res->assign(value);
|
||||
|
||||
delete[] desc; // Free allocated shape info
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_VECTOR(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::vector<GET_SECOND(TYPE)>(sd::LongType length, const GET_SECOND(TYPE) startingValue, \
|
||||
sd::LaunchContext* context); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_COMMON_TYPES), TMPL_INSTANTIATE_VECTOR)
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray *NDArrayFactory::create(const char order, const std::vector<LongType>& shape, LaunchContext* context) {
|
||||
return create(order, shape, DataTypeUtils::fromT<T>(), context);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE(NDArray *NDArrayFactory::create,
|
||||
(const char order, const std::vector<sd::LongType>& shape, sd::LaunchContext* context),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::create(const char order, const std::vector<LongType>& shape, DataType dtype,
|
||||
LaunchContext* context) {
|
||||
if ((int)shape.size() > SD_MAX_RANK)
|
||||
THROW_EXCEPTION("NDArrayFactory::create: rank of NDArray can't exceed 32");
|
||||
|
||||
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor(dtype, order, shape);
|
||||
|
||||
DataBuffer * buffer = new DataBuffer(
|
||||
descriptor->arrLength() * DataTypeUtils::sizeOfElement(dtype), dtype, context->getWorkspace());
|
||||
|
||||
NDArray *result = new NDArray(buffer, descriptor, context);
|
||||
delete descriptor;
|
||||
result->nullify();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NDArray* NDArrayFactory::create_(DataType dtype, LaunchContext* context) {
|
||||
auto result = create(dtype, context);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static NDArray *create(DataType type, const std::vector<LongType>& shape, LaunchContext* context) {
|
||||
auto buffer = new DataBuffer(DataTypeUtils::sizeOfElement(type) * shape::prodLong(shape.data(),shape.size()), type, context->getWorkspace());
|
||||
auto desc = ShapeBuilders::createShapeInfo(type,'c',shape);
|
||||
auto cachedDesc = ConstantShapeHelper::getInstance().bufferForShapeInfo(desc);
|
||||
NDArray *result = new NDArray(buffer, cachedDesc->primary(), context);
|
||||
delete[] desc;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray *NDArrayFactory::create(const std::vector<T>& values, LaunchContext* context) {
|
||||
DataBuffer * buffer =
|
||||
new DataBuffer(values.size() * sizeof(T), DataTypeUtils::fromT<T>(), context->getWorkspace(), true);
|
||||
|
||||
auto desc = ShapeDescriptor::vectorDescriptor(values.size(), DataTypeUtils::fromT<T>());
|
||||
NDArray *res = new NDArray(buffer, desc, context);
|
||||
memcpyFromVector<T>(res->buffer(), values);
|
||||
|
||||
res->tickWriteHost();
|
||||
res->syncToDevice();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define TMPL_INSTANTIATE_CREATE_G(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray* NDArrayFactory::create<GET_SECOND(TYPE)>(const std::vector<GET_SECOND(TYPE)>& values, sd::LaunchContext* context); \
|
||||
))
|
||||
|
||||
ITERATE_LIST((SD_NUMERIC_TYPES), TMPL_INSTANTIATE_CREATE_G)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray* NDArrayFactory::empty_(LaunchContext* context) {
|
||||
auto shapeInfo = ShapeBuilders::createScalarShapeInfo(DataTypeUtils::fromT<T>(), context->getWorkspace());
|
||||
ArrayOptions::setPropertyBit(shapeInfo, ARRAY_EMPTY);
|
||||
auto result = new NDArray(nullptr, shapeInfo, context, false, 0);
|
||||
|
||||
RELEASE(shapeInfo, context->getWorkspace());
|
||||
|
||||
return result;
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE(NDArray* NDArrayFactory::empty_, (sd::LaunchContext * context),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
NDArray* NDArrayFactory::empty_(DataType dataType, LaunchContext* context) {
|
||||
if (context == nullptr) context = LaunchContext ::defaultContext();
|
||||
|
||||
auto shapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, context->getWorkspace());
|
||||
ArrayOptions::setPropertyBit(shapeInfo, ARRAY_EMPTY);
|
||||
auto result = new NDArray(nullptr, shapeInfo, context, false, 0);
|
||||
|
||||
RELEASE(shapeInfo, context->getWorkspace());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray *NDArrayFactory::empty(LaunchContext* context) {
|
||||
return empty(DataTypeUtils::fromT<T>(), context);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE(NDArray *NDArrayFactory::empty, (sd::LaunchContext * context),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
SD_LIB_EXPORT NDArray* NDArrayFactory::empty(DataType dataType, LaunchContext* context) {
|
||||
auto shapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, context->getWorkspace());
|
||||
ArrayOptions::setPropertyBit(shapeInfo, ARRAY_EMPTY);
|
||||
NDArray *result= new NDArray(nullptr, shapeInfo, context, false, 0);
|
||||
|
||||
RELEASE(shapeInfo, context->getWorkspace());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::valueOf(std::vector<LongType>& shape, NDArray& value, const char order,
|
||||
LaunchContext* context) {
|
||||
auto res = create_(order, shape, value.dataType(), context);
|
||||
res->assign(&value);
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::create_(const char order, std::vector<LongType>& shape, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return new NDArray(order, shape, dataType, context);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
NDArray *NDArrayFactory::create(T* buffer, const char order, const std::initializer_list<LongType>& shape,
|
||||
LaunchContext* context) {
|
||||
if ((int)shape.size() > SD_MAX_RANK)
|
||||
THROW_EXCEPTION("NDArrayFactory::create: Rank of NDArray can't exceed 32");
|
||||
|
||||
std::vector<LongType> shp(shape);
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor(DataTypeUtils::fromT<T>(), order, shp);
|
||||
|
||||
DataBuffer * pBuffer = new DataBuffer(
|
||||
buffer, descriptor->arrLength() * sizeof(T), descriptor->dataType(), false, context->getWorkspace());
|
||||
|
||||
NDArray *result = new NDArray(pBuffer, descriptor, context);
|
||||
delete descriptor;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replace TMPL_INSTANTIATE_CREATE_H
|
||||
#define TMPL_INSTANTIATE_CREATE_H(TYPE) \
|
||||
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
|
||||
GET_FIRST(TYPE), \
|
||||
template SD_LIB_EXPORT NDArray *NDArrayFactory::create<GET_SECOND(TYPE)>(GET_SECOND(TYPE)* buffer, const char order, \
|
||||
const std::initializer_list<sd::LongType>& shape, \
|
||||
sd::LaunchContext* context); \
|
||||
))
|
||||
ITERATE_LIST((SD_COMMON_TYPES),TMPL_INSTANTIATE_CREATE_H)
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(HAS_UTF16)
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(const char16_t* u16string, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(u16string, dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const char16_t* u16string, DataType dtype, LaunchContext* context) {
|
||||
return string_(std::u16string(u16string), dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const std::u16string& u16string, DataType dtype, LaunchContext* context) {
|
||||
auto res = new NDArray(u16string, dtype, context);
|
||||
return res;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string(const std::u16string& u16string, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(u16string, dtype, context);
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_UTF32)
|
||||
#if defined(HAS_UTF32)
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(const char32_t* u32string, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(u32string, dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const char32_t* u32string, DataType dtype, LaunchContext* context) {
|
||||
return string_(std::u32string(u32string), dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const std::u32string& u32string, DataType dtype, LaunchContext* context) {
|
||||
auto res = new NDArray(u32string, dtype, context);
|
||||
return res;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray * NDArrayFactory::string(const std::u32string& u32string, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(u32string, dtype, context);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#if defined(HAS_UTF8)
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(const char* str, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(str, dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const char* str, DataType dtype, LaunchContext* context) {
|
||||
return string_(std::string(str), dtype, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(const std::string& str, DataType dtype, LaunchContext* context) {
|
||||
auto res = new NDArray(str, dtype, context);
|
||||
return res;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(const std::string& str, DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(str, dtype, context);
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_UTF8)
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<const char*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, strings, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<const char*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
std::vector<std::string> vec(strings.size());
|
||||
int cnt = 0;
|
||||
for (auto s : strings) vec[cnt++] = std::string(s);
|
||||
|
||||
return string_(shape, vec, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<std::string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, string, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<std::string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
auto res = new NDArray(shape, string, dataType, context);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_UTF16)
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape,
|
||||
const std::initializer_list<const char16_t*>& strings, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return new NDArray(shape, std::vector<const char16_t*>(strings), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<const char16_t*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, strings, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string(std::vector<LongType>& shape,
|
||||
const std::initializer_list<std::u16string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, std::vector<std::u16string>(string), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape,
|
||||
const std::initializer_list<const char16_t*>& strings, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return string_(shape, std::vector<const char16_t*>(strings), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<const char16_t*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
std::vector<std::u16string> vec(strings.size());
|
||||
int cnt = 0;
|
||||
for (auto s : strings) vec[cnt++] = std::u16string(s);
|
||||
|
||||
return string_(shape, vec, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape,
|
||||
const std::initializer_list<std::u16string>& string, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return string_(shape, std::vector<std::u16string>(string), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<std::u16string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
auto res = new NDArray(shape, string, dataType, context);
|
||||
return res;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray * NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<std::u16string>& string,
|
||||
DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(shape, string, dtype, context);
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_UTF32)
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape,
|
||||
const std::initializer_list<const char32_t*>& strings, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return new NDArray(shape, std::vector<const char32_t*>(strings), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray * NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<const char32_t*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, strings, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray *NDArrayFactory::string(std::vector<LongType>& shape,
|
||||
const std::initializer_list<std::u32string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
return new NDArray(shape, std::vector<std::u32string>(string), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape,
|
||||
const std::initializer_list<const char32_t*>& strings, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return string_(shape, std::vector<const char32_t*>(strings), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<const char32_t*>& strings,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
std::vector<std::u32string> vec(strings.size());
|
||||
int cnt = 0;
|
||||
for (auto s : strings) vec[cnt++] = std::u32string(s);
|
||||
return string_(shape, vec, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape,
|
||||
const std::initializer_list<std::u32string>& string, DataType dataType,
|
||||
LaunchContext* context) {
|
||||
return string_(shape, std::vector<std::u32string>(string), dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray* NDArrayFactory::string_(std::vector<LongType>& shape, const std::vector<std::u32string>& string,
|
||||
DataType dataType, LaunchContext* context) {
|
||||
// Default constructor was deleted to prevent uninitialized NDArrays (nullptr _shapeInfo)
|
||||
return new NDArray(shape, string, dataType, context);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
NDArray * NDArrayFactory::string(std::vector<LongType>& shape, const std::vector<std::u32string>& string,
|
||||
DataType dtype, LaunchContext* context) {
|
||||
return new NDArray(shape, string, dtype, context);
|
||||
}
|
||||
#endif
|
||||
|
||||
NDArray NDArrayFactory::fromNpyFile(const char* fileName) {
|
||||
auto size = getFileSize(fileName);
|
||||
if (size < 0) THROW_EXCEPTION("File doesn't exit");
|
||||
|
||||
auto pNPY = reinterpret_cast<char*>(numpyFromFile(std::string(fileName)));
|
||||
|
||||
auto nBuffer = reinterpret_cast<void*>(dataPointForNumpy(pNPY));
|
||||
auto shape = reinterpret_cast<LongType*>(shapeBufferForNumpy(pNPY));
|
||||
|
||||
auto length = shape::length(shape);
|
||||
int8_t* buffer = nullptr;
|
||||
memory::Workspace* workspace = nullptr;
|
||||
auto byteLen = length * DataTypeUtils::sizeOfElement(ArrayOptions::dataType(shape));
|
||||
|
||||
ALLOCATE(buffer, workspace, byteLen, int8_t);
|
||||
memcpy(buffer, nBuffer, byteLen);
|
||||
|
||||
free(pNPY);
|
||||
|
||||
return NDArray(buffer, shape, LaunchContext::defaultContext(), true, 0);
|
||||
}
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,278 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
|
||||
#include <array/NDArrayList.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
#include <ops/declarable/helpers/stack.h>
|
||||
|
||||
#include <iterator>
|
||||
#if NOT_EXCLUDED(OP_stack)
|
||||
namespace sd {
|
||||
NDArrayList::NDArrayList(int height, bool expandable) {
|
||||
_expandable = expandable;
|
||||
_elements.store(0);
|
||||
_counter.store(0);
|
||||
_id.first = 0;
|
||||
_id.second = 0;
|
||||
_height = height;
|
||||
sd_debug("\nCreating NDArrayList\n","");
|
||||
}
|
||||
|
||||
NDArrayList::~NDArrayList() {
|
||||
sd_debug("\nDeleting NDArrayList: [%i]\n", _chunks.size());
|
||||
for (auto const& v : _chunks) delete v.second;
|
||||
|
||||
_chunks.clear();
|
||||
}
|
||||
|
||||
NDArray* NDArrayList::read(int idx) { return new NDArray(readRaw(idx)->dup()); }
|
||||
|
||||
sd::DataType NDArrayList::dataType() { return _dtype; }
|
||||
|
||||
NDArray* NDArrayList::readRaw(int idx) {
|
||||
if (_chunks.count(idx) < 1) {
|
||||
sd_debug("Non-existent chunk requested: [%i]\n", idx);
|
||||
THROW_EXCEPTION("Bad index");
|
||||
}
|
||||
|
||||
return _chunks[idx];
|
||||
}
|
||||
|
||||
|
||||
NDArray* NDArrayList::remove(int idx) {
|
||||
if(!isWritten(idx)) {
|
||||
sd_debug("Non-existent chunk requested: [%i]\n", idx);
|
||||
THROW_EXCEPTION("Bad index");
|
||||
}
|
||||
|
||||
delete _chunks[idx];
|
||||
|
||||
_elements--;
|
||||
return new NDArray(readRaw(idx)->dup());
|
||||
}
|
||||
|
||||
|
||||
sd::Status NDArrayList::write(int idx, NDArray* array) {
|
||||
if (_chunks.count(idx) == 0)
|
||||
_elements++;
|
||||
else {
|
||||
delete _chunks[idx];
|
||||
}
|
||||
|
||||
// we store reference shape on first write
|
||||
if (_chunks.empty()) {
|
||||
_dtype = array->dataType();
|
||||
|
||||
if (_shape.empty()) {
|
||||
// adding leading 1 to shape
|
||||
_shape.emplace_back(1);
|
||||
for (int e = 0; e < array->rankOf(); e++) _shape.emplace_back(array->sizeAt(e));
|
||||
} else {
|
||||
// if shape is inferred (say, from split_list)
|
||||
if (static_cast<size_t>(array->rankOf()) == _shape.size()) {
|
||||
// skipping first dim
|
||||
for (size_t e = 1; e < _shape.size(); e++) {
|
||||
if (_shape[e] != array->sizeAt(e))
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
}
|
||||
} else if (static_cast<size_t>(array->rankOf()) == _shape.size() - 1) {
|
||||
// case like 2d _shape, and 1D rows
|
||||
for (size_t e = 1; e < _shape.size(); e++)
|
||||
if (_shape[e] != array->sizeAt(e - 1))
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
} else
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
}
|
||||
} else {
|
||||
if (array->dataType() != _dtype)
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT, "NDArrayList: all arrays must have same data type");
|
||||
|
||||
// if shape is inferred (say, from split_list)
|
||||
if (static_cast<size_t>(array->rankOf()) == _shape.size()) {
|
||||
// skipping first dim
|
||||
for (size_t e = 1; e < _shape.size(); e++) {
|
||||
if (_shape[e] != array->sizeAt(e))
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
}
|
||||
} else if (static_cast<size_t>(array->rankOf()) == _shape.size() - 1) {
|
||||
// case like 2d _shape, and 1D rows
|
||||
for (size_t e = 1; e < _shape.size(); e++)
|
||||
if (_shape[e] != array->sizeAt(e - 1))
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
} else
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT,
|
||||
"NDArrayList: all arrays must have same size along inner dimensions");
|
||||
}
|
||||
|
||||
|
||||
// storing reference
|
||||
_chunks[idx] = array;
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
std::vector<sd::LongType>& NDArrayList::shape() { return _shape; }
|
||||
|
||||
int NDArrayList::counter() { return _counter++; }
|
||||
|
||||
void NDArrayList::unstack(NDArray* array, LongType axis) {
|
||||
_axis = axis;
|
||||
std::vector<sd::LongType> args({axis});
|
||||
auto newAxis = ShapeUtils::evalDimsToExclude(array->rankOf(),1, args.data());
|
||||
auto result = array->allTensorsAlongDimension(*newAxis);
|
||||
for (sd::LongType e = 0; e < result.size(); e++) {
|
||||
auto chunk = result.at(e);
|
||||
write(e, new NDArray(chunk->dup(array->ordering())));
|
||||
}
|
||||
|
||||
delete newAxis;
|
||||
}
|
||||
|
||||
NDArray* NDArrayList::stack() {
|
||||
int numElements = _elements.load();
|
||||
if(numElements < 1) {
|
||||
return new NDArray(NDArrayFactory::empty<double>());
|
||||
|
||||
}
|
||||
std::vector<NDArray*> inputs(numElements);
|
||||
for (int e = 0; e < numElements; e++) {
|
||||
if(!_chunks[e]->isEmpty())
|
||||
_chunks[e]->syncToDevice();
|
||||
inputs[e] = _chunks[e];
|
||||
}
|
||||
|
||||
if(inputs[0] == nullptr) {
|
||||
THROW_EXCEPTION("First input element was a null ptr!");
|
||||
}
|
||||
|
||||
auto inShapeInfo = inputs[0]->shapeInfo();
|
||||
int rank = shape::rank(inShapeInfo);
|
||||
NDArray* array = nullptr;
|
||||
|
||||
if (shape::isEmptyConst(inShapeInfo)) {
|
||||
switch (rank) {
|
||||
case 0: {
|
||||
if (numElements == 1) {
|
||||
std::vector<sd::LongType> shape = {0};
|
||||
array = new NDArray(inputs[0]->ordering(), shape, ArrayOptions::dataType(inShapeInfo), inputs[0]->getContext());
|
||||
} else {
|
||||
std::vector<sd::LongType> shape = {(sd::LongType)numElements, 0};
|
||||
array = new NDArray('c', shape, ArrayOptions::dataType(inShapeInfo),
|
||||
inputs[0]->getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
std::vector<sd::LongType> outShape(inShapeInfo + 1, inShapeInfo + 1 + rank);
|
||||
outShape.insert(outShape.begin(), (sd::LongType)numElements);
|
||||
array =
|
||||
new NDArray(shape::order(inShapeInfo), outShape, ArrayOptions::dataType(inShapeInfo), inputs[0]->getContext());
|
||||
}
|
||||
|
||||
ops::helpers::stack(inputs[0]->getContext(), inputs, *array, 0);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
std::pair<int, int>& NDArrayList::id() { return _id; }
|
||||
|
||||
std::string& NDArrayList::name() { return _name; }
|
||||
|
||||
sd::LaunchContext* NDArrayList::context() { return _context; }
|
||||
|
||||
int NDArrayList::elements() { return _elements.load(); }
|
||||
|
||||
int NDArrayList::height() {
|
||||
return (int)_chunks.size();
|
||||
}
|
||||
|
||||
bool NDArrayList::isWritten(int index) {
|
||||
if (_chunks.count(index) > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
NDArray* NDArrayList::pick(std::initializer_list<LongType> indices) {
|
||||
std::vector<LongType> idcs(indices);
|
||||
return pick(idcs);
|
||||
}
|
||||
|
||||
NDArray* NDArrayList::pick(std::vector<LongType>& indices) {
|
||||
std::vector<sd::LongType> shape(_shape);
|
||||
|
||||
shape[_axis] = indices.size();
|
||||
// do we have to enforce C order here?
|
||||
auto array = new NDArray('c', shape, _chunks[0]->dataType(), _context);
|
||||
const sd::LongType *axis2 = const_cast<sd::LongType *>(&_axis);
|
||||
std::vector<sd::LongType> *axis = ShapeUtils::evalDimsToExclude(shape.size(),1, axis2);
|
||||
auto tads = array->allTensorsAlongDimension(*axis);
|
||||
int indicesSize = indices.size();
|
||||
|
||||
if (tads.size() != indicesSize) THROW_EXCEPTION("Number of TADs should match number of indices");
|
||||
|
||||
for (int e = 0; e < indicesSize; e++) tads.at(e)->assign(_chunks[indices[e]]);
|
||||
|
||||
delete axis;
|
||||
return array;
|
||||
}
|
||||
|
||||
NDArrayList* NDArrayList::clone() {
|
||||
auto list = new NDArrayList(_height, _expandable);
|
||||
list->_axis = _axis;
|
||||
list->_id.first = _id.first;
|
||||
list->_id.second = _id.second;
|
||||
list->_name = _name;
|
||||
list->_elements.store(_elements.load());
|
||||
|
||||
for (auto const& v : _chunks) {
|
||||
list->_chunks[v.first] = new NDArray(v.second->dup());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool NDArrayList::equals(NDArrayList& other) {
|
||||
if (_axis != other._axis) return false;
|
||||
|
||||
if (_chunks.size() != other._chunks.size()) return false;
|
||||
|
||||
for (auto const& v : _chunks) {
|
||||
if (other._chunks.count(v.first) == 0) return false;
|
||||
|
||||
auto arrThis = _chunks[v.first];
|
||||
auto arrThat = other._chunks[v.first];
|
||||
|
||||
if (!arrThis->equalsTo(arrThat)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/PointerDeallocator.h>
|
||||
|
||||
namespace sd {
|
||||
|
||||
void PointerDeallocator::release(void *ptr) {
|
||||
// noop
|
||||
}
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/PointerWrapper.h>
|
||||
|
||||
namespace sd {
|
||||
PointerWrapper::PointerWrapper(void *ptr, const std::shared_ptr<PointerDeallocator> &deallocator)
|
||||
: _pointer(ptr), _deallocator(deallocator) {
|
||||
//
|
||||
}
|
||||
|
||||
PointerWrapper::~PointerWrapper() {
|
||||
if (_deallocator.get() != nullptr) _deallocator->release(_pointer);
|
||||
}
|
||||
|
||||
void *PointerWrapper::pointer() const { return _pointer; }
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * 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.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/PrimaryPointerDeallocator.h>
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
#include <array/ShapeCacheLifecycleTracker.h>
|
||||
#endif
|
||||
|
||||
namespace sd {
|
||||
|
||||
void PrimaryPointerDeallocator::release(void *ptr) {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// Track shape cache deallocation before freeing
|
||||
sd::array::ShapeCacheLifecycleTracker::getInstance().recordDeallocation(
|
||||
reinterpret_cast<LongType*>(ptr));
|
||||
#endif
|
||||
|
||||
// Root cause of SIGSEGV crashes: shape buffers are allocated with new[] but were being
|
||||
// freed with delete (single-object), causing heap corruption and undefined behavior.
|
||||
delete[] reinterpret_cast<int8_t *>(ptr);
|
||||
}
|
||||
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,146 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <graph/FlatUtils.h>
|
||||
|
||||
namespace sd {
|
||||
ResultSet::ResultSet() {
|
||||
//
|
||||
}
|
||||
|
||||
ResultSet::ResultSet(const ::graph::FlatResult* result) {
|
||||
for (size_t e = 0; e < result->variables()->size(); e++) {
|
||||
auto var = result->variables()->Get(e);
|
||||
|
||||
NDArray* array;
|
||||
|
||||
if (var->ndarray() != nullptr) {
|
||||
array = graph::FlatUtils::fromFlatArray(var->ndarray());
|
||||
} else if (var->shape() != nullptr) {
|
||||
std::vector<LongType> shapeInfo;
|
||||
for (size_t i = 0; i < var->shape()->size(); i++) {
|
||||
shapeInfo.emplace_back(var->shape()->Get(i));
|
||||
}
|
||||
|
||||
// we just create empty array here
|
||||
int s0 = shapeInfo.at(0);
|
||||
|
||||
std::vector<LongType> shape;
|
||||
for (int i = 0; i < s0; i++) {
|
||||
shape.emplace_back(shapeInfo.at(i + 1));
|
||||
}
|
||||
|
||||
array =
|
||||
new NDArray((char)shapeInfo.at(shapeInfo.size() - 1), shape, DataTypeUtils::fromFlatDataType(var->dtype()));
|
||||
} else {
|
||||
sd_printf("Either shape or NDArray should be defined in FlatResult variable\n", "");
|
||||
THROW_EXCEPTION("Empty variable");
|
||||
}
|
||||
|
||||
_content.push_back(array);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet::ResultSet(const ResultSet& other) noexcept {
|
||||
for (const auto v : other._content) _content.emplace_back(v);
|
||||
|
||||
_status = other._status;
|
||||
_removable = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// move constructor
|
||||
ResultSet::ResultSet(ResultSet&& other) noexcept {
|
||||
_content = std::move(other._content);
|
||||
_status = other._status;
|
||||
_removable = other._removable;
|
||||
other._removable = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// move assignment operator
|
||||
ResultSet& ResultSet::operator=(ResultSet&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
delContent();
|
||||
|
||||
_content = std::move(other._content);
|
||||
|
||||
_status = other._status;
|
||||
_removable = other._removable;
|
||||
other._removable = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResultSet& ResultSet::operator=(const ResultSet& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
delContent();
|
||||
|
||||
for (const auto v : other._content) _content.push_back(v);
|
||||
|
||||
_status = other._status;
|
||||
_removable = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ResultSet::delContent() {
|
||||
if (_removable) {
|
||||
std::vector<DataBuffer *> deleted;
|
||||
for (auto v : _content) {
|
||||
auto buffer = v->dataBuffer();
|
||||
deleted.push_back(buffer);
|
||||
if (!v->isView() && v->shapeInfo() != nullptr) {
|
||||
delete v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet::~ResultSet() { delContent(); }
|
||||
|
||||
void ResultSet::printIndexedBuffers() {
|
||||
for (size_t e = 0; e < _content.size(); e++) {
|
||||
auto array = _content.at(e);
|
||||
auto strVal = "Array e: " + std::to_string(e) + " is: ";
|
||||
array->printIndexedBuffer(strVal.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
void ResultSet::setNonRemovable() { _removable = false; }
|
||||
|
||||
int ResultSet::size() { return (int)_content.size(); }
|
||||
|
||||
NDArray* ResultSet::at(const unsigned long idx) const { return _content.at(idx); }
|
||||
|
||||
NDArray* ResultSet::operator[](const unsigned long idx) const { return _content[idx]; }
|
||||
|
||||
void ResultSet::push_back(NDArray* array) { _content.emplace_back(array); }
|
||||
|
||||
Status ResultSet::status() { return _status; }
|
||||
|
||||
void ResultSet::setStatus(Status status) { _status = status; }
|
||||
|
||||
void ResultSet::purge() { _content.clear(); }
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,873 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
// @author AbdelRauf
|
||||
|
||||
#include <array/ShapeDescriptor.h>
|
||||
#include <helpers/ShapeBuilders.h>
|
||||
#include <helpers/shape.h>
|
||||
#include <helpers/ModularHasher.h>
|
||||
#include "helpers/ShapeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// equal to operator
|
||||
bool ShapeDescriptor::operator==(const ShapeDescriptor& other) const {
|
||||
// First check scalar values to fail fast
|
||||
if (_rank != other._rank ||
|
||||
_dataType != other._dataType ||
|
||||
_order != other._order ||
|
||||
_extraProperties != other._extraProperties ||
|
||||
_offset != other._offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle null pointers
|
||||
if (_shape_strides == nullptr && other._shape_strides == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (_shape_strides == nullptr || other._shape_strides == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare shape and strides
|
||||
const int total_length = (_rank < 1 ? 1 : _rank) * 2;
|
||||
return memcmp(_shape_strides, other._shape_strides, total_length * sizeof(LongType)) == 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// less than operator
|
||||
bool ShapeDescriptor::operator<(const ShapeDescriptor &other) const {
|
||||
return std::tie(_extraProperties, _rank, _dataType, _order, _shape_strides) <
|
||||
std::tie(other._extraProperties, other._rank, other._dataType, other._order, other._shape_strides);
|
||||
}
|
||||
|
||||
LongType *ShapeDescriptor::toShapeInfo() const {
|
||||
// for empty array use original
|
||||
return ShapeBuilders::createShapeInfoFrom(const_cast<ShapeDescriptor *>(this));
|
||||
}
|
||||
|
||||
ShapeDescriptor::~ShapeDescriptor() {
|
||||
// no-op
|
||||
if(_shape_strides != nullptr && this->ownsShapeStrides) {
|
||||
delete[] _shape_strides;
|
||||
_shape_strides = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LongType ShapeDescriptor::offset() {
|
||||
return _offset;
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const char order, const LongType *shape, const LongType rank)
|
||||
: _rank(rank), _order(order), _dataType(type) {
|
||||
int rank2 = rank < 1 ? 1 : rank;
|
||||
_shape_strides = new LongType[2 * rank2];
|
||||
this->ownsShapeStrides = true;
|
||||
if(order != 'c' && order != 'f') {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Invalid ordering from shape buffer";
|
||||
errorMessage += std::to_string(order);
|
||||
delete[] _shape_strides;
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
|
||||
}
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
delete[] _shape_strides;
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
auto _shape = _shape_strides;
|
||||
for (int i = 0; i < rank2; i++) {
|
||||
_shape[i] = shape[i];
|
||||
}
|
||||
|
||||
_extraProperties = ArrayOptions::flagForDataType(type);
|
||||
|
||||
fillStrides();
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const char order, const LongType *shape,
|
||||
const LongType *strides, const LongType rank, LongType extras = -1) {
|
||||
if(shape == nullptr)
|
||||
THROW_EXCEPTION("ShapeDescriptor constructor: Shape can not be null!");
|
||||
_shape_strides = nullptr;
|
||||
ownsShapeStrides = false;
|
||||
if(type == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
ownsShapeStrides = true;
|
||||
//note this used to operate directly on the vector buffer
|
||||
//it now does manual copies with more checks.
|
||||
//this is to handle the 0 length case.
|
||||
if(rank < 1) {
|
||||
_shape_strides = new LongType[2 * rank];
|
||||
_dataType = type;
|
||||
_order = order;
|
||||
_rank = rank;
|
||||
_extraProperties = extras;
|
||||
} else {
|
||||
_shape_strides = new LongType [2 * rank];
|
||||
_dataType = type;
|
||||
_order = order;
|
||||
_rank = rank;
|
||||
_extraProperties = extras;
|
||||
auto _shape = _shape_strides;
|
||||
auto _strides = _shape_strides + rank;
|
||||
for (int e = 0; e < rank; e++) {
|
||||
_shape[e] = shape[e];
|
||||
if(rank > 1 && shape[e] == 0 && !ArrayOptions::hasPropertyBitSet(_extraProperties, ARRAY_EMPTY)) {
|
||||
_extraProperties = ArrayOptions::setPropertyBitForFlagsValue(_extraProperties, ARRAY_EMPTY);
|
||||
}
|
||||
if(strides != nullptr)
|
||||
_strides[e] = strides[e];
|
||||
}
|
||||
|
||||
if(strides == nullptr)
|
||||
fillStrides();
|
||||
}
|
||||
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape)
|
||||
: _order(order), _dataType(type) {
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
_rank = shape.size();
|
||||
_extraProperties = ArrayOptions::defaultFlag();
|
||||
_extraProperties = ArrayOptions::setDataTypeValue(_extraProperties, type);
|
||||
int rank2 = shape.size() < 1 ? 1 : shape.size();
|
||||
_shape_strides = new LongType [2 * rank2];
|
||||
this->ownsShapeStrides = true;
|
||||
if(_rank > 0) {
|
||||
auto _shape = _shape_strides;
|
||||
for (int i = 0; i < _rank; i++) {
|
||||
_shape[i] = shape[i];
|
||||
if(shape[i] == 0 && !ArrayOptions::hasPropertyBitSet(_extraProperties, ARRAY_EMPTY)) {
|
||||
_extraProperties = ArrayOptions::setPropertyBitForFlagsValue(_extraProperties, ARRAY_EMPTY);
|
||||
}
|
||||
}
|
||||
fillStrides();
|
||||
}
|
||||
|
||||
_order = order;
|
||||
if(_order != 'c' && _order != 'f') {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Invalid ordering from shape buffer";
|
||||
errorMessage += std::to_string(_order);
|
||||
delete[] _shape_strides;
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
|
||||
}
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
delete[] _shape_strides;
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape,
|
||||
const std::vector<LongType> &strides, const LongType ews)
|
||||
: ShapeDescriptor(type, order, shape, strides) {
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const LongType length)
|
||||
: _rank(1), _order('c'), _dataType(type), _extraProperties(0) {
|
||||
_shape_strides = new LongType [2];
|
||||
_shape_strides[0] = length;
|
||||
_shape_strides[1] = 1; //{shape, stride}
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const LongType *shapeInfo, bool validateDataType, bool overrideStrides) {
|
||||
if(shapeInfo == nullptr) {
|
||||
THROW_EXCEPTION("ShapeDescriptor constructor: Shape info cannot be null!");
|
||||
}
|
||||
|
||||
|
||||
sd::LongType rankVal = shape::rank(shapeInfo);
|
||||
if(rankVal < 0 || rankVal > SD_MAX_RANK) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Shape descriptor created with invalid rank: ";
|
||||
errorMessage += std::to_string(rankVal);
|
||||
errorMessage += ". Valid range is 0 to ";
|
||||
errorMessage += std::to_string(SD_MAX_RANK);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
|
||||
if(rankVal == 0) {
|
||||
//detect when the shape buffer values are unset.
|
||||
auto len = shape::shapeInfoLength(rankVal);
|
||||
//min number of values in a shape info buffer
|
||||
bool allZero = true;
|
||||
for(int i = 0; i < len; i++) {
|
||||
if(shapeInfo[i] != 0) {
|
||||
allZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allZero) {
|
||||
THROW_EXCEPTION("Found shape buffer with all zero values. Values likely unset.");
|
||||
}
|
||||
}
|
||||
|
||||
_shape_strides = nullptr;
|
||||
_order = shape::order(shapeInfo);
|
||||
this->ownsShapeStrides = true;
|
||||
if(_order != 'c' && _order != 'f') {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Invalid ordering from shape buffer";
|
||||
errorMessage += std::to_string(_order);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
|
||||
}
|
||||
|
||||
_rank = static_cast<sd::LongType >(rankVal);
|
||||
_extraProperties = shape::extra(shapeInfo);
|
||||
_dataType = ArrayOptions::dataType(shapeInfo);
|
||||
|
||||
if(_rank > 0 && shape::isEmptyConst(shapeInfo)) {
|
||||
_shape_strides = new LongType[2 * _rank];
|
||||
auto _strides = _shape_strides + _rank;
|
||||
auto shapePtr = shape::shapeOf(shapeInfo);
|
||||
for (LongType e = 0; e < _rank; e++) {
|
||||
_shape_strides[e] = shapePtr[e];
|
||||
_strides[e] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (_rank > 0 && !shape::isEmptyConst(shapeInfo)) {
|
||||
_shape_strides = new LongType[2 * rankVal];
|
||||
auto _strides = _shape_strides + _rank;
|
||||
auto shapePtr = shape::shapeOf(shapeInfo);
|
||||
auto stridePtr = shape::stride(shapeInfo);
|
||||
if(overrideStrides) {
|
||||
LongType *stridesNew = shape::order(shapeInfo) == 'c' ? shape::calcStrides(shapePtr, rankVal) : shape::calcStridesFortran(shapePtr, rankVal);
|
||||
for (LongType e = 0; e < _rank; e++) {
|
||||
_shape_strides[e] = shapePtr[e];
|
||||
_shape_strides[e + _rank] = stridesNew[e];
|
||||
}
|
||||
|
||||
delete[] stridesNew;
|
||||
|
||||
} else {
|
||||
for (LongType e = 0; e < _rank; e++) {
|
||||
_shape_strides[e] = shapePtr[e];
|
||||
_shape_strides[e + _rank] = stridePtr[e];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//validate construction of the shape descriptor. This is to prevent flag regressions when modifying
|
||||
//_extraProperties.
|
||||
//ensure that we only validate this for array size > 1
|
||||
if(!ArrayOptions::hasPropertyBitSet(_extraProperties, ARRAY_EMPTY) && this->arrLength() > 1) {
|
||||
for(int i = 0; i < _rank; i++) {
|
||||
if(_strides[i] == 0 && shapePtr[i] != 1) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Shape descriptor:";
|
||||
errorMessage += toString();
|
||||
errorMessage += "Array set as not empty but stride is not 0. Index is ";
|
||||
errorMessage += std::to_string(i);
|
||||
errorMessage += " Stride is ";
|
||||
errorMessage += std::to_string(_strides[i]);
|
||||
//append the full _shape_strides data
|
||||
errorMessage += " _shape_strides is ";
|
||||
for(int j = 0; j < _rank * 2; j++) {
|
||||
errorMessage += std::to_string(_shape_strides[j]);
|
||||
if(j < _rank * 2 - 1) {
|
||||
errorMessage += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
}
|
||||
} else if(this->arrLength() > 1) {
|
||||
for(int i = 0; i < _rank; i++) {
|
||||
if(_strides[i] != 0) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Array set as not empty but stride is 0. Index is";
|
||||
errorMessage += std::to_string(i);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if(!shape::isEmptyConst(shapeInfo)) { // Handle scalar case
|
||||
_shape_strides = new LongType [2]; // Since we're setting shape and stride
|
||||
_shape_strides[0] = 0; // Shape for scalar
|
||||
_shape_strides[1] = 1; // Stride for scalar
|
||||
} else {
|
||||
_shape_strides = new LongType[2];
|
||||
_shape_strides[0] = 0;
|
||||
_shape_strides[1] = 0;
|
||||
}
|
||||
_order = shape::order(shapeInfo);
|
||||
_dataType = ArrayOptions::dataType(shapeInfo);
|
||||
if(validateDataType && _dataType == UNKNOWN) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Shape descriptor created with invalid data type ";
|
||||
errorMessage += DataTypeUtils::asString(_dataType);
|
||||
errorMessage += " extra properties for data type was ";
|
||||
errorMessage += DataTypeUtils::asString(ArrayOptions::dataTypeValue(_extraProperties));
|
||||
errorMessage += " Underlying extra value was ";
|
||||
errorMessage += std::to_string(_extraProperties);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const LongType *shapeInfo, const DataType dtypeOverride, const bool overrideStrides)
|
||||
: ShapeDescriptor(shapeInfo, false, overrideStrides) {
|
||||
if(dtypeOverride == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
_dataType = dtypeOverride;
|
||||
_order = shape::order(shapeInfo);
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
//data type has already been set by another constructor. We need to update the _extraProperties
|
||||
//to reflect the new data type. This is effectively a cast.
|
||||
_extraProperties = ArrayOptions::propertyWithoutDataTypeValue(_extraProperties);
|
||||
_extraProperties = ArrayOptions::setDataTypeValue(_extraProperties, dtypeOverride);
|
||||
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const LongType *shapeInfo, const LongType *dtypeOverride)
|
||||
: ShapeDescriptor(shapeInfo, ArrayOptions::dataType(dtypeOverride), false) {
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const LongType *shapeInfo, const LongType *dtypeOverride,
|
||||
const LongType *orderOverride)
|
||||
: ShapeDescriptor(shapeInfo, ArrayOptions::dataType(dtypeOverride), false) {
|
||||
_order = shape::order(orderOverride);
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int ShapeDescriptor::rank() const { return _rank; }
|
||||
|
||||
|
||||
LongType ShapeDescriptor::arrLength() const {
|
||||
if(_shape_strides== nullptr) {
|
||||
return 0;
|
||||
}
|
||||
// when _ews == 1 allocation length is also array length
|
||||
LongType len = 1;
|
||||
for (int i = 0; i < _rank; i++) len *= _shape_strides[i];
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void ShapeDescriptor::print() const {
|
||||
printf("ShapeDescriptor: [");
|
||||
for (int i = 0; i < _rank; i++) {
|
||||
printf("%lld", _shape_strides[i]);
|
||||
if (i < _rank - 1) printf(", ");
|
||||
}
|
||||
printf("], [");
|
||||
for (int i = _rank; i < 2 * _rank; i++) {
|
||||
printf("%lld", _shape_strides[i]);
|
||||
if (i < 2 * _rank - 1) printf(", ");
|
||||
}
|
||||
printf("], %c, %s, %lld\n", _order, DataTypeUtils::asString(_dataType).c_str(), _extraProperties);
|
||||
}
|
||||
|
||||
LongType ShapeDescriptor::allocLength() const {
|
||||
if (_paddedAllocSize > 0) return _paddedAllocSize;
|
||||
auto _shape = _shape_strides;
|
||||
auto _strides = _shape_strides + _rank;
|
||||
int rank2 = _rank < 1 ? 1 : _rank;
|
||||
|
||||
LongType len = 1;
|
||||
if (_rank > 1) {
|
||||
// calculate using max stride
|
||||
int ind = _order == 'c' ? 0 : rank2 - 1;
|
||||
return _shape[ind] * _strides[ind];
|
||||
}
|
||||
for (int i = 0; i < rank2; i++) {
|
||||
len += (_shape[i] - 1) * _strides[i];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void ShapeDescriptor::collectStoreStackTrace() {
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
this->storeStackTrace = backward::StackTrace();
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->storeStackTrace.load_here(32);
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - storeStackTrace will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->storeStackTrace.load_here(32);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
LongType ShapeDescriptor::validate() const {
|
||||
auto status = SHAPE_DESC_OK;
|
||||
bool is_continous = true;
|
||||
//exclude scalars on purpose here
|
||||
if (_rank > 0 || _rank > SD_MAX_RANK) status |= SHAPE_DESC_INCORRECT_RANK;
|
||||
auto _shape = _shape_strides;
|
||||
auto _strides = _shape_strides + _rank;
|
||||
if(_order != 'c' && _order != 'f') {
|
||||
THROW_EXCEPTION("Invalid ordering from shape buffer");
|
||||
}
|
||||
|
||||
bool hasZero = false;
|
||||
for (int i = 0; i < _rank; i++) {
|
||||
if (_shape[i] == 0) {
|
||||
hasZero = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//this check isn't correct for vectors
|
||||
if (_rank > 0 && !shape::isVector(_shape_strides,2) && !hasZero) {
|
||||
if (_order == 'c') {
|
||||
for (int j = _rank - 2; j >= 0; j--) {
|
||||
LongType currentStride = _strides[j];
|
||||
LongType allowedStride = _strides[j + 1] * _shape[j + 1];
|
||||
if (currentStride < allowedStride) {
|
||||
status = status | SHAPE_DESC_INCORRECT_STRIDES;
|
||||
break;
|
||||
}
|
||||
is_continous = is_continous & (currentStride == allowedStride);
|
||||
}
|
||||
} else {
|
||||
for (int j = 1; j < _rank; j++) {
|
||||
LongType currentStride = _strides[j];
|
||||
LongType allowedStride = _strides[j - 1] * _shape[j - 1];
|
||||
if (currentStride < allowedStride) {
|
||||
status = status | SHAPE_DESC_INCORRECT_STRIDES;
|
||||
break;
|
||||
}
|
||||
is_continous = is_continous & (currentStride == allowedStride);
|
||||
}
|
||||
}
|
||||
|
||||
int index = (_order == 'c') ? _rank - 1 : 0;
|
||||
auto correctEws = is_continous ? _strides[index] : 0;
|
||||
}
|
||||
|
||||
if(isEmpty()) {
|
||||
for(int i = 0; i < _rank; i++) {
|
||||
if(_strides[i] != 0) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Array set as empty but stride is not 0. Index is ";
|
||||
errorMessage += std::to_string(i);
|
||||
errorMessage += " Stride is ";
|
||||
errorMessage += std::to_string(_strides[i]);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
char ShapeDescriptor::order() const { return _order; }
|
||||
|
||||
DataType ShapeDescriptor::dataType() const {
|
||||
if(!DataTypeUtils::validDataType(_dataType)) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Shape descriptor created with invalid data type";
|
||||
errorMessage += DataTypeUtils::asString(_dataType);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
return _dataType;
|
||||
}
|
||||
|
||||
bool ShapeDescriptor::isEmpty() const { return (_extraProperties & ARRAY_EMPTY) == ARRAY_EMPTY; }
|
||||
bool ShapeDescriptor::isScalar() const { return (!isEmpty() && rank() == 0) || (rank() == 1 && arrLength() == 1); }
|
||||
|
||||
sd::LongType * ShapeDescriptor::shape_strides() { return _shape_strides; }
|
||||
|
||||
const LongType *ShapeDescriptor::stridesPtr() const {
|
||||
return _shape_strides == nullptr ? nullptr : _shape_strides + _rank;
|
||||
}
|
||||
|
||||
ShapeDescriptor::ShapeDescriptor(const ShapeDescriptor &other) {
|
||||
_rank = other._rank;
|
||||
_extraProperties = other._extraProperties;
|
||||
if(other._dataType == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
_dataType = other._dataType;
|
||||
_order = other._order;
|
||||
_shape_strides = other._shape_strides;
|
||||
this->ownsShapeStrides = false;
|
||||
_paddedAllocSize = other._paddedAllocSize;
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ShapeDescriptor::ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape,
|
||||
const std::vector<LongType> &strides)
|
||||
: _order(order), _dataType(type) {
|
||||
_rank = shape.size();
|
||||
int rank2 = _rank < 1 ? 1 : _rank;
|
||||
|
||||
_shape_strides = new LongType [2 * rank2];
|
||||
this->ownsShapeStrides = true;
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
// - backward-cpp's backtrace() is NOT safe during very early JVM initialization
|
||||
#ifdef __cpp_exceptions
|
||||
try {
|
||||
this->st.load_here();
|
||||
} catch (...) {
|
||||
// Stack trace capture failed - st will remain empty (size() == 0)
|
||||
}
|
||||
#else
|
||||
this->st.load_here();
|
||||
#endif
|
||||
#endif
|
||||
auto _shape = _shape_strides;
|
||||
auto _strides = _shape_strides + rank2;
|
||||
if (!shape.empty() && strides.size() != shape.size() ) {
|
||||
for (int i = 0; i < rank2; i++) {
|
||||
_shape[i] = shape[i];
|
||||
}
|
||||
fillStrides();
|
||||
} else {
|
||||
for (int i = 0; i < rank2; i++) {
|
||||
_shape[i] = shape[i];
|
||||
_strides[i] = strides[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShapeDescriptor * ShapeDescriptor::emptyDescriptor(const DataType type) {
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor();
|
||||
if(type == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
descriptor->_dataType = type;
|
||||
descriptor->_extraProperties = ARRAY_EMPTY | ArrayOptions::flagForDataType(type);
|
||||
descriptor->_rank = 0;
|
||||
descriptor->_order = 'c';
|
||||
descriptor->ownsShapeStrides = true;
|
||||
descriptor->_shape_strides = new LongType [1];
|
||||
descriptor->_shape_strides[0] = 0;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
ShapeDescriptor * ShapeDescriptor::scalarDescriptor(const DataType type) {
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor();
|
||||
if(type == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
descriptor->_dataType = type;
|
||||
descriptor->_extraProperties = ArrayOptions::flagForDataType(type);
|
||||
descriptor->_rank = 0;
|
||||
descriptor->_order = 'c';
|
||||
descriptor->ownsShapeStrides = true;
|
||||
descriptor->_shape_strides = new LongType [2];
|
||||
descriptor->_shape_strides[0] = 0;
|
||||
descriptor->_shape_strides[1] = 1;
|
||||
descriptor->_offset = 0;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
ShapeDescriptor * ShapeDescriptor::vectorDescriptor(const LongType length, const DataType type) {
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor();
|
||||
if(type == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
|
||||
descriptor->_dataType = type;
|
||||
descriptor->_shape_strides = new LongType [2];
|
||||
descriptor->_shape_strides[0] = length;
|
||||
descriptor->_shape_strides[1] = 0;
|
||||
descriptor->ownsShapeStrides = true;
|
||||
descriptor->_offset = 0;
|
||||
if (length > 0) {
|
||||
descriptor->_shape_strides[1] = 1;
|
||||
descriptor->_extraProperties = ArrayOptions::flagForDataType(type);
|
||||
}
|
||||
else {
|
||||
descriptor->_shape_strides[1] = 0;
|
||||
descriptor->_extraProperties = ARRAY_EMPTY;
|
||||
descriptor->_extraProperties = ArrayOptions::setDataTypeValue(descriptor->_extraProperties, type);
|
||||
}
|
||||
|
||||
descriptor->_order = 'c';
|
||||
descriptor->_rank = 1;
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
ShapeDescriptor * ShapeDescriptor::paddedBufferDescriptor(const DataType type, const char order,
|
||||
const std::vector<LongType> &shape,
|
||||
const std::vector<LongType> &paddings) {
|
||||
ShapeDescriptor *descriptor = new ShapeDescriptor();
|
||||
if(type == UNKNOWN)
|
||||
THROW_EXCEPTION("Shape descriptor created with invalid data type");
|
||||
|
||||
descriptor->_dataType = type;
|
||||
descriptor->_order = order;
|
||||
descriptor->_rank = shape.size();
|
||||
descriptor->_extraProperties = ArrayOptions::flagForDataType(type);
|
||||
descriptor->ownsShapeStrides = true;
|
||||
if (descriptor->_rank < 1) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
int rank2 = descriptor->_rank < 1 ? 1 : descriptor->_rank;
|
||||
|
||||
descriptor->_shape_strides = new LongType [2 * rank2];
|
||||
auto _shape = descriptor->_shape_strides;
|
||||
auto _strides = descriptor->_shape_strides + rank2;
|
||||
for (size_t i = 0; i < shape.size(); i++) {
|
||||
_shape[i] = shape[i];
|
||||
}
|
||||
// calculate strides with paddings
|
||||
int min_rank = static_cast<size_t>(descriptor->_rank) > paddings.size() ? paddings.size() : rank2;
|
||||
bool is_continous = true;
|
||||
if (order == 'c') {
|
||||
_strides[rank2 - 1] = 1L;
|
||||
for (int j = descriptor->_rank - 2; j >= 0; j--) {
|
||||
LongType pad = (j + 1 < min_rank) ? paddings[j + 1] : 0;
|
||||
_strides[j] = _strides[j + 1] * (_shape[j + 1] + pad);
|
||||
descriptor->_extraProperties = descriptor->_extraProperties | (_shape[j + 1] == 0);
|
||||
if (pad != 0) is_continous = false;
|
||||
}
|
||||
if (!is_continous && descriptor->_rank > 0) {
|
||||
LongType size_pad = paddings.size() > 0 ? paddings[0] : 0;
|
||||
// alloc size should be supplied manually as we dont have place to store it
|
||||
descriptor->_paddedAllocSize = _strides[0] * (_shape[0] + size_pad);
|
||||
}
|
||||
} else {
|
||||
_strides[0] = 1L;
|
||||
for (int j = 1; j < rank2; j++) {
|
||||
LongType pad = (j - 1 < min_rank) ? paddings[j - 1] : 0;
|
||||
_strides[j] = _strides[j - 1] * (_shape[j - 1] + pad);
|
||||
descriptor->_extraProperties = descriptor->_extraProperties | (_shape[j - 1] == 0);
|
||||
if (pad != 0) is_continous = false;
|
||||
}
|
||||
if (!is_continous && descriptor->_rank > 0) {
|
||||
LongType size_pad = paddings.size() >= static_cast<size_t>(descriptor->_rank) ? paddings[descriptor->_rank - 1] : 0;
|
||||
// alloc size should be supplied manually as we dont have place to store it
|
||||
descriptor->_paddedAllocSize = _strides[descriptor->_rank - 1] * (_shape[descriptor->_rank - 1] + size_pad);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_continous) descriptor->_extraProperties |= ARRAY_HAS_PADDED_BUFFER;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
} // namespace sd
|
||||
|
||||
namespace std {
|
||||
size_t hash<sd::ShapeDescriptor>::operator()(sd::ShapeDescriptor k) const {
|
||||
// Check cache first
|
||||
if (k._hash_computed) {
|
||||
return k._cached_hash;
|
||||
}
|
||||
// Check cache first
|
||||
if (k._hash_computed) {
|
||||
return k._cached_hash;
|
||||
}
|
||||
|
||||
using namespace sd::helpers::detail;
|
||||
|
||||
// Combine order and dataType into initial hash
|
||||
uint64_t hash = ModularHasher::combine_hashes({
|
||||
static_cast<uint64_t>(k.order()),
|
||||
static_cast<uint64_t>(k.dataType()) << 8
|
||||
});
|
||||
|
||||
// Hash the shape and strides array
|
||||
sd::LongType* shape_strides = k.shape_strides();
|
||||
const int stop = k.rank() * 2;
|
||||
|
||||
if (shape_strides != nullptr) {
|
||||
hash = DataChunkHasher<sd::LongType>::hash_data(
|
||||
shape_strides,
|
||||
stop,
|
||||
hash
|
||||
);
|
||||
}
|
||||
|
||||
// Cache the computed hash
|
||||
k._cached_hash = hash;
|
||||
k._hash_computed = true;
|
||||
|
||||
return hash;
|
||||
}
|
||||
} // namespace std
|
||||
@@ -0,0 +1,91 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
|
||||
#include <array/ShapeList.h>
|
||||
|
||||
namespace sd {
|
||||
|
||||
|
||||
ShapeList::ShapeList(LongType* shape) {
|
||||
if (shape != nullptr) push_back(shape);
|
||||
}
|
||||
|
||||
ShapeList::~ShapeList() {
|
||||
if (_autoremovable) destroy();
|
||||
}
|
||||
|
||||
ShapeList::ShapeList(const std::vector< LongType*>& shapes, bool isWorkspace)
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < shapes.size(); i++) {
|
||||
push_back(shapes[i]);
|
||||
}
|
||||
_workspace = isWorkspace;
|
||||
}
|
||||
|
||||
ShapeList::ShapeList(const std::vector< LongType*>& shapes) {
|
||||
_shapes = shapes;
|
||||
}
|
||||
|
||||
void ShapeList::destroy() {
|
||||
|
||||
if (_destroyed) return;
|
||||
if (!_workspace) {
|
||||
for (int i = 0; i < size(); i++){
|
||||
if (_shapes[i] != nullptr) delete[] _shapes[i];
|
||||
}
|
||||
}
|
||||
_destroyed = true;
|
||||
}
|
||||
|
||||
int ShapeList::size() const {
|
||||
return (int)_shapes.size();
|
||||
}
|
||||
|
||||
LongType* ShapeList::at(int idx) {
|
||||
if(idx < 0) {
|
||||
idx += _shapes.size();
|
||||
}
|
||||
|
||||
if (size() <= idx) {
|
||||
std::string errorMessage;
|
||||
errorMessage += "Can't find requested variable by index: ";
|
||||
errorMessage += std::to_string(idx);
|
||||
THROW_EXCEPTION(errorMessage.c_str());
|
||||
}
|
||||
|
||||
return _shapes[idx];
|
||||
}
|
||||
|
||||
void ShapeList::push_back( LongType* shape) {
|
||||
_shapes.push_back(shape);
|
||||
}
|
||||
|
||||
void ShapeList::detach() {
|
||||
for (int e = 0; e < size(); e++) {
|
||||
_shapes[e] = shape::detachShape(_shapes[e]);
|
||||
}
|
||||
|
||||
_autoremovable = true;
|
||||
_workspace = false;
|
||||
}
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,79 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
|
||||
#include "../TadDescriptor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <helpers/ModularHasher.h>
|
||||
namespace sd {
|
||||
TadDescriptor::TadDescriptor(const TadDescriptor &other) {
|
||||
_originalShape = other._originalShape;
|
||||
_axis = other._axis;
|
||||
_unitiesInShape = other._unitiesInShape;
|
||||
}
|
||||
|
||||
TadDescriptor::TadDescriptor(const LongType *originalShape, const LongType *dimensions, const LongType length,
|
||||
const bool keepUnitiesInShape) {
|
||||
|
||||
_axis.resize(length);
|
||||
for (LongType e = 0; e < length; e++) {
|
||||
_axis[e] = dimensions[e];
|
||||
}
|
||||
|
||||
if (length > 1) std::sort(_axis.begin(), _axis.end());
|
||||
|
||||
_originalShape = const_cast<sd::LongType *>(originalShape);
|
||||
_unitiesInShape = keepUnitiesInShape;
|
||||
}
|
||||
|
||||
bool TadDescriptor::operator==(const TadDescriptor &other) const {
|
||||
return std::tie(_originalShape, _axis, _unitiesInShape) ==
|
||||
std::tie(other._originalShape, other._axis, other._unitiesInShape);
|
||||
}
|
||||
|
||||
bool TadDescriptor::operator<(const TadDescriptor &other) const {
|
||||
return std::tie(_originalShape, _axis, _unitiesInShape) <
|
||||
std::tie(other._originalShape, other._axis, other._unitiesInShape);
|
||||
}
|
||||
|
||||
std::vector<LongType> &TadDescriptor::axis() { return _axis; }
|
||||
|
||||
LongType *TadDescriptor::originalShape() { return _originalShape; }
|
||||
|
||||
bool TadDescriptor::areUnitiesinShape() const { return _unitiesInShape; }
|
||||
} // namespace sd
|
||||
|
||||
namespace std {
|
||||
size_t hash<sd::TadDescriptor>::operator()(const sd::TadDescriptor &k) const {
|
||||
using namespace sd::helpers::detail;
|
||||
|
||||
// Start with initial hash from unities flag
|
||||
uint64_t hash = ModularHasher::hash_scalar(k.areUnitiesinShape());
|
||||
// Hash the axis vector
|
||||
auto& axes = const_cast<sd::TadDescriptor&>(k).axis();
|
||||
if (!axes.empty()) {
|
||||
hash = ModularHasher::hash_vector(axes, hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
} // namespace std
|
||||
@@ -0,0 +1,252 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License, Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* See the NOTICE file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
* 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.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include "../TadPack.h"
|
||||
|
||||
#include <helpers/shape.h>
|
||||
#include <system/Environment.h>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE)
|
||||
#include <array/TADCacheLifecycleTracker.h>
|
||||
#endif
|
||||
|
||||
namespace sd {
|
||||
TadPack::TadPack( ConstantShapeBuffer *shapes,
|
||||
ConstantOffsetsBuffer *offets, LongType numTads,
|
||||
LongType* dimensions, LongType dimLength)
|
||||
: _tadShape(shapes),
|
||||
_tadOffsets(offets) {
|
||||
_numTads = numTads;
|
||||
_dimensionsLength = dimLength;
|
||||
if(dimensions != nullptr) {
|
||||
_dimensions = new LongType[dimLength];
|
||||
for(int i = 0; i < dimLength; i++) {
|
||||
_dimensions[i] = dimensions[i];
|
||||
}
|
||||
}
|
||||
|
||||
computeHash();
|
||||
|
||||
#if defined(SD_GCC_FUNCTRACE) && !defined(__JAVACPP_HACK__)
|
||||
// Track TAD cache allocation - only capture stack trace if tracking is enabled
|
||||
// Stack trace capture is expensive, so skip it when tracking is disabled
|
||||
auto& tracker = sd::array::TADCacheLifecycleTracker::getInstance();
|
||||
if (tracker.isEnabled()) {
|
||||
// Capture stack trace for this TadPack allocation
|
||||
_stackTrace = backward::StackTrace();
|
||||
_stackTrace.load_here(32);
|
||||
}
|
||||
|
||||
// Always record allocation for basic counting (cheap operation)
|
||||
size_t shape_info_bytes = 0;
|
||||
size_t offsets_bytes = 0;
|
||||
|
||||
if (_tadShape != nullptr && _tadShape->primary() != nullptr) {
|
||||
shape_info_bytes = shape::shapeInfoByteLength(_tadShape->primary());
|
||||
}
|
||||
|
||||
if (_tadOffsets != nullptr && _tadOffsets->primary() != nullptr) {
|
||||
offsets_bytes = _numTads * sizeof(LongType);
|
||||
}
|
||||
|
||||
std::vector<LongType> dims;
|
||||
if (_dimensions != nullptr) {
|
||||
dims.assign(_dimensions, _dimensions + _dimensionsLength);
|
||||
}
|
||||
|
||||
tracker.recordAllocation(this, _numTads, shape_info_bytes, offsets_bytes, dims);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
TadPack::~TadPack() {
|
||||
#if defined(SD_GCC_FUNCTRACE) && !defined(__JAVACPP_HACK__)
|
||||
// Track TAD cache deallocation before cleanup.
|
||||
// Guard must match constructor guard for proper allocation/deallocation pairing.
|
||||
sd::array::TADCacheLifecycleTracker::getInstance().recordDeallocation(this);
|
||||
#endif
|
||||
|
||||
// Clean up dimensions array that was allocated in constructor
|
||||
if (_dimensions != nullptr) {
|
||||
delete[] _dimensions;
|
||||
_dimensions = nullptr;
|
||||
}
|
||||
|
||||
// Clean up TAD offsets buffer if we own it
|
||||
// This is owned when transferred via releaseOffsets() from TadCalculator
|
||||
if (_tadOffsets != nullptr) {
|
||||
delete _tadOffsets;
|
||||
_tadOffsets = nullptr;
|
||||
}
|
||||
|
||||
// DON'T delete _tadShape - it comes from ConstantShapeHelper cache
|
||||
}
|
||||
|
||||
LongType* TadPack::primaryShapeInfo() {
|
||||
if(_tadShape->primary() == nullptr)
|
||||
THROW_EXCEPTION("TadPack::primaryShapeInfo: primary shape info is nullptr!");
|
||||
return _tadShape->primary();
|
||||
}
|
||||
|
||||
LongType* TadPack::primaryOffsets() {
|
||||
return _tadOffsets->primary();
|
||||
}
|
||||
|
||||
LongType* TadPack::specialShapeInfo() { return _tadShape->special(); }
|
||||
|
||||
LongType* TadPack::specialOffsets() { return _tadOffsets->special(); }
|
||||
|
||||
LongType TadPack::numberOfTads() const { return _numTads; }
|
||||
|
||||
LongType* TadPack::platformShapeInfo() {
|
||||
return Environment::getInstance().isCPU() ? primaryShapeInfo() : specialShapeInfo();
|
||||
}
|
||||
|
||||
LongType* TadPack::platformOffsets() {
|
||||
return Environment::getInstance().isCPU() ? primaryOffsets() : specialOffsets();
|
||||
}
|
||||
|
||||
|
||||
void TadPack::print(const char* msg) {
|
||||
printf("---------------------------\n");
|
||||
printf("%s: ", msg);
|
||||
printf("Offsets:\n");
|
||||
for (int e = 0; e < _numTads; e++) {
|
||||
printf("%lld, ", _tadOffsets->primary()[e]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Dimensions:\n");
|
||||
if (_dimensions == nullptr || _dimensionsLength == 0) {
|
||||
printf("none\n");
|
||||
} else {
|
||||
for (int i = 0; i < _dimensionsLength; i++) {
|
||||
printf("%lld, ", _dimensions[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("tad pack shape info:");
|
||||
shape::printShapeInfo(_tadShape->primary());
|
||||
printf("\n");
|
||||
printf("number of tads: %lld\n", _numTads);
|
||||
printf("shape info length: %lld\n", _shapeInfoLength);
|
||||
printf("---------------------------\n");
|
||||
}
|
||||
|
||||
LongType TadPack::shapeInfoLength() { return shape::shapeInfoLength(primaryShapeInfo()); }
|
||||
bool TadPack::operator==( TadPack& other) {
|
||||
// Compare number of TADs
|
||||
if (_numTads != other._numTads)
|
||||
return false;
|
||||
|
||||
// Compare shape information
|
||||
LongType* thisShape = primaryShapeInfo();
|
||||
LongType* otherShape = other.primaryShapeInfo();
|
||||
|
||||
// Check for null shape info
|
||||
if ((thisShape == nullptr) != (otherShape == nullptr))
|
||||
return false;
|
||||
|
||||
if (thisShape != nullptr) {
|
||||
// Compare rank
|
||||
const int thisRank = shape::rank(thisShape);
|
||||
const int otherRank = shape::rank(otherShape);
|
||||
if (thisRank != otherRank)
|
||||
return false;
|
||||
|
||||
// Compare shape order
|
||||
if (shape::order(thisShape) != shape::order(otherShape))
|
||||
return false;
|
||||
|
||||
// Compare data type
|
||||
if (ArrayOptions::dataType(thisShape) != ArrayOptions::dataType(otherShape))
|
||||
return false;
|
||||
|
||||
// Compare shape dimensions
|
||||
for (int i = 0; i < thisRank; i++) {
|
||||
if (shape::shapeOf(thisShape)[i] != shape::shapeOf(otherShape)[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare shape strides
|
||||
for (int i = 0; i < thisRank; i++) {
|
||||
if (shape::stride(thisShape)[i] != shape::stride(otherShape)[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare dimensions array
|
||||
if ((_dimensions == nullptr) != (other._dimensions == nullptr))
|
||||
return false;
|
||||
|
||||
if (_dimensions != nullptr) {
|
||||
if (_dimensionsLength != other._dimensionsLength)
|
||||
return false;
|
||||
|
||||
for (LongType i = 0; i < _dimensionsLength; i++) {
|
||||
if (_dimensions[i] != other._dimensions[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare offsets
|
||||
LongType* thisOffsets = primaryOffsets();
|
||||
LongType* otherOffsets = other.primaryOffsets();
|
||||
|
||||
// Check for null offsets
|
||||
if ((thisOffsets == nullptr) != (otherOffsets == nullptr))
|
||||
return false;
|
||||
|
||||
if (thisOffsets != nullptr) {
|
||||
for (LongType i = 0; i < _numTads; i++) {
|
||||
if (thisOffsets[i] != otherOffsets[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TadPack::getStackTraceAsString() const {
|
||||
#if defined(SD_GCC_FUNCTRACE) && !defined(__JAVACPP_HACK__)
|
||||
// Use backward::Printer to format the stack trace into a string
|
||||
std::ostringstream oss;
|
||||
backward::Printer p;
|
||||
p.snippet = false; // Don't include source code snippets
|
||||
p.address = true; // Include addresses
|
||||
p.object = false; // Don't include object file info
|
||||
p.color_mode = backward::ColorMode::never; // No ANSI colors in string
|
||||
|
||||
// Print to our string stream (we need to cast away const to use _stackTrace)
|
||||
// This is safe since print doesn't modify the StackTrace
|
||||
backward::StackTrace& mutable_st = const_cast<backward::StackTrace&>(_stackTrace);
|
||||
p.print(mutable_st, oss);
|
||||
|
||||
return oss.str();
|
||||
#else
|
||||
return ""; // Return empty string when functrace is not enabled
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace sd
|
||||
Reference in New Issue
Block a user