chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
+196
View File
@@ -0,0 +1,196 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef ND4J_ARRAY_OPTIONS_H
#define ND4J_ARRAY_OPTIONS_H
#pragma once
#include <system/common.h>
#include <system/op_boilerplate.h>
#include <array/ArrayType.h>
#include <array/DataType.h>
#include <array/SpaceType.h>
#include <array/SparseType.h>
#include <initializer_list>
#include <vector>
//notice how each flag value is multiplied by 2
//if they are too close in value values will clash.
#define ARRAY_SPARSE 2
#define ARRAY_COMPRESSED 4
#define ARRAY_EMPTY 8
#define ARRAY_RAGGED 16
#define ARRAY_CSR 32
#define ARRAY_CSC 64
#define ARRAY_COO 128
// complex values
#define ARRAY_COMPLEX 512
// quantized values
#define ARRAY_QUANTIZED 1024
// 16 bit float FP16
#define ARRAY_HALF 4096
// 16 bit bfloat16
#define ARRAY_BHALF 2048
// regular 32 bit float
#define ARRAY_FLOAT 8192
// regular 64 bit float
#define ARRAY_DOUBLE 16384
// 8 bit integer
#define ARRAY_CHAR 32768
// 16 bit integer
#define ARRAY_SHORT 65536
// 32 bit integer
#define ARRAY_INT 131072
// 64 bit integer
#define ARRAY_LONG 262144
// boolean values
#define ARRAY_BOOL 524288
// UTF values
#define ARRAY_UTF8 1048576
#define ARRAY_UTF16 4194304
#define ARRAY_UTF32 16777216
// flag for extras
#define ARRAY_EXTRAS 2097152
// flag for signed/unsigned integers
#define ARRAY_UNSIGNED 8388608
// flag for arrays with padded buffer
#define ARRAY_HAS_PADDED_BUFFER (1 << 25)
//flags for when array has a view or not
#define ARRAY_IS_VIEW 33554432
//flag for when array needs a copy
//this is mainly used in the reshape_no_copy op but could be used elsewhere
#define ARRAY_NEEDS_COPY 67108864
//we need this in order to preserve the offset of the original buffer when creating the output array
//when views are created, sometimes we need to use the original offset of the array
//we don't need this very often and we don't store the offset in the shape info
//this preserves the offsets only being in the ndarray but allowing us to pass information
//when creating views, each flag is for an input in to an op, most of the time we only need the first 3
//but may need more. For now only the first one is used but may be needed elsewhere.
#define ARRAY_COPY_OFFSET_INPUT_0 134217728
#define ARRAY_COPY_OFFSET_INPUT_1 268435456
#define ARRAY_COPY_OFFSET_INPUT_2 536870912
#define ARRAY_COPY_OFFSET_INPUT_3 1073741824
#define ARRAY_COPY_OFFSET_INPUT_4 2147483648
#define ARRAY_COPY_OFFSET_INPUT_5 4294967296
#define ARRAY_COPY_OFFSET_INPUT_6 8589934592
#define ARRAY_COPY_OFFSET_INPUT_7 17179869184
#define ARRAY_COPY_OFFSET_INPUT_8 34359738368
#define ARRAY_COPY_OFFSET_INPUT_9 68719476736
#define ARRAY_COPY_OFFSET_INPUT_10 137438953472
#define DEFAULT_FLAG 0
namespace sd {
class SD_LIB_EXPORT ArrayOptions {
public:
static SD_HOST SD_INLINE LongType extra(const LongType *shapeInfo);
static SD_HOST SD_INLINE void setExtra(LongType *shapeInfo, LongType value);
static SD_HOST SD_INLINE bool isNewFormat(const LongType *shapeInfo);
static SD_HOST SD_INLINE bool hasPropertyBitSet(const LongType *shapeInfo, LongType property);
static SD_HOST SD_INLINE bool togglePropertyBit(LongType *shapeInfo, LongType property);
static SD_HOST SD_INLINE void unsetPropertyBit(LongType *shapeInfo, LongType property);
static SD_HOST SD_INLINE void validateSingleDataType(LongType property);
static SD_HOST SD_INLINE void setPropertyBit(LongType *shapeInfo, LongType property);
static SD_HOST SD_INLINE void setPropertyBits(LongType *shapeInfo, std::initializer_list<LongType> properties);
static SD_HOST SD_INLINE sd::LongType numDataTypesSet(sd::LongType property);
static SD_HOST SD_INLINE bool isUnsigned(LongType *shapeInfo);
static SD_HOST SD_INLINE bool isSparseArray(sd::LongType *shapeInfo);
static SD_HOST SD_INLINE DataType dataType(const LongType *shapeInfo);
static SD_HOST SD_INLINE SpaceType spaceType(LongType *shapeInfo);
static SD_INLINE SD_HOST_DEVICE SpaceType spaceType(const LongType *shapeInfo);
static SD_HOST SD_INLINE ArrayType arrayType(LongType *shapeInfo);
static SD_HOST SD_INLINE ArrayType arrayType(const LongType *shapeInfo);
static SD_HOST SD_INLINE bool isView(LongType *shapeInfo);
static SD_HOST SD_INLINE void toggleIsView(LongType *shapeInfo);
static SD_INLINE SD_HOST_DEVICE SparseType sparseType(LongType *shapeInfo);
static SD_HOST SD_INLINE SparseType sparseType(const LongType *shapeInfo);
static SD_INLINE SD_HOST_DEVICE bool hasExtraProperties(LongType *shapeInfo);
static SD_HOST SD_INLINE bool hasPaddedBuffer(const LongType *shapeInfo);
static SD_HOST SD_INLINE void flagAsPaddedBuffer(LongType *shapeInfo);
static SD_HOST SD_INLINE void resetDataType(LongType *shapeInfo);
static SD_HOST SD_INLINE LongType propertyWithoutDataType(const LongType *shapeInfo);
static SD_HOST SD_INLINE void setDataType(LongType *shapeInfo, const DataType dataType);
static SD_HOST SD_INLINE LongType setDataTypeValue(LongType extraStorage, const DataType dataType);
static SD_HOST SD_INLINE LongType flagForDataType(const DataType dataType);
static SD_HOST SD_INLINE void copyDataType(LongType *to, const LongType *from);
static SD_HOST SD_INLINE const char *enumerateSetFlags(const LongType *shapeInfo);
static SD_HOST SD_INLINE void unsetAllFlags(LongType *shapeInfo);
static SD_HOST SD_INLINE int enumerateSetFlags(const LongType *shapeInfo, const char **setFlagsOutput, int maxFlags);
static SD_HOST SD_INLINE const char *findFlagString(int flag);
static SD_HOST SD_INLINE LongType extraIndex(const LongType *shapeInfo);
static SD_HOST SD_INLINE LongType extraIndex(LongType *shapeInfo);
static SD_HOST SD_INLINE void unsetAllFlags(LongType &flagStorage);
static SD_HOST SD_INLINE const char *enumerateSetFlagsForFlags(const LongType flagStorage);
static SD_HOST SD_INLINE SpaceType spaceTypeForFlags(const LongType &flagStorage);
static SD_HOST SD_INLINE ArrayType arrayTypeForFlags(const LongType &flagStorage);
static SD_HOST SD_INLINE bool togglePropertyBitForFlags(LongType &flagStorage, LongType property);
static SD_HOST SD_INLINE LongType unsetPropertyBitForFlags(LongType &flagStorage, LongType property);
static SD_HOST SD_INLINE SparseType sparseTypeForFlags(const LongType &flagStorage);
static SD_INLINE LongType setPropertyBitForFlagsValue(LongType extraStorage, LongType property);
static SD_HOST SD_INLINE bool hasPropertyBitSet(const LongType extra, LongType property);
static SD_HOST SD_INLINE void resetFlags(LongType *to);
static SD_HOST SD_INLINE LongType defaultFlag();
static SD_HOST SD_INLINE LongType propertyWithoutDataTypeValue(LongType extra);
static SD_HOST SD_INLINE DataType dataTypeValue(LongType property);
static SD_INLINE bool isEmpty(LongType *shapeInfo);
static SD_INLINE void toggleIsEmpty(LongType *shapeInfo);
static SD_INLINE bool arrayNeedsCopy(LongType *shapeInfo);
static SD_INLINE void toggleArrayNeedsCopy(LongType *shapeInfo);
};
}
#endif // ND4J_ARRAY_OPTIONS_H :)
+799
View File
@@ -0,0 +1,799 @@
#ifndef ND4J_ARRAY_OPTIONS_HXX
#define ND4J_ARRAY_OPTIONS_HXX
#include <array/ArrayOptions.h>
#include <system/op_boilerplate.h>
#include <helpers/shape.h>
#include <array/DataTypeUtils.h>
#pragma once
namespace sd {
SD_HOST SD_INLINE sd::LongType ArrayOptions::extraIndex(const sd::LongType *shapeInfo) {
return ArrayOptions::extraIndex(const_cast<sd::LongType *>(shapeInfo));
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::extraIndex(sd::LongType *shapeInfo) {
if(shapeInfo == nullptr)
THROW_EXCEPTION("Shape info was null!");
sd::LongType rank = shapeInfo[0];
sd::LongType idx = 0;
//rank takes up 1 element + usual elements
if(rank == 0)
idx = 3;
else
// FIXME magic numbers
idx = rank + rank + 1;
return idx;
}
SD_HOST SD_INLINE void ArrayOptions::setExtra(sd::LongType *shapeInfo, sd::LongType value) {
sd::LongType idx = ArrayOptions::extraIndex(shapeInfo);
shapeInfo[idx] = value;
}
SD_HOST SD_INLINE LongType ArrayOptions::extra(const LongType *shapeInfo) {
sd::LongType idx = ArrayOptions::extraIndex(shapeInfo);
if(idx > shape::shapeInfoLength(shape::rank(shapeInfo)))
THROW_EXCEPTION("Extra index is out of bounds!");
return shapeInfo[idx];
}
SD_HOST SD_INLINE bool ArrayOptions::isNewFormat(const sd::LongType *shapeInfo) {
return (extra(const_cast<sd::LongType *>(shapeInfo)) != 0);
}
SD_HOST SD_INLINE bool ArrayOptions::isSparseArray(sd::LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, ARRAY_SPARSE);
}
SD_HOST_DEVICE SD_INLINE bool ArrayOptions::hasExtraProperties(sd::LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, ARRAY_EXTRAS);
}
SD_HOST SD_INLINE bool ArrayOptions::hasPropertyBitSet(const sd::LongType extra, LongType property) {
return ((static_cast<int>(extra) & static_cast<int>(property)) == static_cast<int>(property));
}
SD_HOST SD_INLINE bool ArrayOptions:: hasPropertyBitSet(const sd::LongType *shapeInfo, LongType property) {
if (!isNewFormat(shapeInfo)) return false;
return ((static_cast<int>(extra(const_cast<sd::LongType *>(shapeInfo)) & property)) == static_cast<int>(property));
}
SD_HOST_DEVICE SD_INLINE bool hasPropertyBitSetForFlags(const sd::LongType& flagStorage, LongType property) {
return static_cast<sd::LongType>(flagStorage & (property)) == (property);
}
SD_HOST SD_INLINE void unsetPropertyBitForFlags(sd::LongType& flagStorage, LongType property) {
flagStorage &= ~property;
}
SD_HOST SD_INLINE const char *ArrayOptions::enumerateSetFlagsForFlags(const LongType flagStorage) {
int maxFlags = 24;
int flagsArray[] = {
ARRAY_SPARSE,
ARRAY_COMPRESSED,
ARRAY_EMPTY,
ARRAY_RAGGED,
ARRAY_CSR,
ARRAY_CSC,
ARRAY_COO,
ARRAY_COMPLEX,
ARRAY_QUANTIZED,
ARRAY_HALF,
ARRAY_BHALF,
ARRAY_FLOAT,
ARRAY_DOUBLE,
ARRAY_CHAR,
ARRAY_SHORT,
ARRAY_INT,
ARRAY_LONG,
ARRAY_BOOL,
ARRAY_UTF8,
ARRAY_UTF16,
ARRAY_UTF32,
ARRAY_EXTRAS,
ARRAY_UNSIGNED,
ARRAY_HAS_PADDED_BUFFER,
ARRAY_IS_VIEW
};
const char* flagsStrings[] = {
"ARRAY_SPARSE",
"ARRAY_COMPRESSED",
"ARRAY_EMPTY",
"ARRAY_RAGGED",
"ARRAY_CSR",
"ARRAY_CSC",
"ARRAY_COO",
"ARRAY_COMPLEX",
"ARRAY_QUANTIZED",
"ARRAY_HALF",
"ARRAY_BHALF",
"ARRAY_FLOAT",
"ARRAY_DOUBLE",
"ARRAY_CHAR",
"ARRAY_SHORT",
"ARRAY_INT",
"ARRAY_LONG",
"ARRAY_BOOL",
"ARRAY_UTF8",
"ARRAY_UTF16",
"ARRAY_UTF32",
"ARRAY_EXTRAS",
"ARRAY_UNSIGNED",
"ARRAY_HAS_PADDED_BUFFER",
"ARRAY_IS_VIEW"
};
std::string flags;
for (int i = 0; i < maxFlags; i++) {
if (hasPropertyBitSetForFlags(flagStorage, flagsArray[i])) {
flags += flagsStrings[i];
flags += " ";
}
}
auto ret = new std::string(flags); // Returns the number of set flags found
return ret->c_str();
}
SD_HOST SD_INLINE void ArrayOptions::unsetAllFlags(sd::LongType& flagStorage) {
int flagsArray[] = {
ARRAY_SPARSE,
ARRAY_COMPRESSED,
ARRAY_EMPTY,
ARRAY_RAGGED,
ARRAY_CSR,
ARRAY_CSC,
ARRAY_COO,
ARRAY_COMPLEX,
ARRAY_QUANTIZED,
ARRAY_HALF,
ARRAY_BHALF,
ARRAY_FLOAT,
ARRAY_DOUBLE,
ARRAY_CHAR,
ARRAY_SHORT,
ARRAY_INT,
ARRAY_LONG,
ARRAY_BOOL,
ARRAY_UTF8,
ARRAY_UTF16,
ARRAY_UTF32,
ARRAY_EXTRAS,
ARRAY_UNSIGNED,
ARRAY_HAS_PADDED_BUFFER
};
for (int i = 0; i < 24; i++) {
unsetPropertyBitForFlags(flagStorage, flagsArray[i]);
}
}
SD_HOST SD_INLINE const char *ArrayOptions::enumerateSetFlags(const sd::LongType *shapeInfo) {
return enumerateSetFlagsForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE void ArrayOptions::unsetAllFlags(sd::LongType *shapeInfo) {
ArrayOptions::unsetAllFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE bool ArrayOptions::isUnsigned(sd::LongType *shapeInfo) {
if (!isNewFormat(shapeInfo)) return false;
return hasPropertyBitSet(shapeInfo, ARRAY_UNSIGNED);
}
#define DATA_TYPE_FLAGS { \
ARRAY_FLOAT, \
ARRAY_DOUBLE, \
ARRAY_HALF, \
ARRAY_BHALF, \
ARRAY_BOOL, \
ARRAY_CHAR, \
ARRAY_SHORT, \
ARRAY_INT, \
ARRAY_LONG, \
ARRAY_UTF8, \
ARRAY_UTF16, \
ARRAY_UTF32 \
}
#define DATA_TYPES { \
sd::DataType::FLOAT32, \
sd::DataType::DOUBLE, \
sd::DataType::HALF, \
sd::DataType::BFLOAT16, \
sd::DataType::BOOL, \
sd::DataType::INT8, \
sd::DataType::INT16, \
sd::DataType::INT32, \
sd::DataType::INT64, \
sd::DataType::UTF8, \
sd::DataType::UTF16, \
sd::DataType::UTF32 \
}
#define ARRAY_UNSIGNED_TYPES { \
ARRAY_CHAR, \
ARRAY_SHORT, \
ARRAY_INT, \
ARRAY_LONG, \
ARRAY_UTF8, \
ARRAY_UTF16, \
ARRAY_UTF32 \
}
#define UNSIGNED_DATA_TYPES { \
sd::DataType::UINT8, \
sd::DataType::UINT16, \
sd::DataType::UINT32, \
sd::DataType::UINT64, \
sd::DataType::UTF8, \
sd::DataType::UTF16, \
sd::DataType::UTF32 \
}
SD_HOST SD_INLINE sd::DataType ArrayOptions::dataTypeValue(sd::LongType property) {
const sd::LongType dataTypeFlags[] = DATA_TYPE_FLAGS;
const sd::DataType dataTypes[] = DATA_TYPES;
const size_t numTypes = sizeof(dataTypeFlags) / sizeof(sd::LongType);
if (hasPropertyBitSetForFlags(property, ARRAY_UNSIGNED)) {
const sd::LongType unsignedTypeFlags[] = ARRAY_UNSIGNED_TYPES;
const sd::DataType unsignedDataTypes[] = UNSIGNED_DATA_TYPES;
const size_t numUnsignedTypes = sizeof(unsignedTypeFlags) / sizeof(sd::LongType);
for (size_t i = 0; i < numUnsignedTypes; ++i) {
if (hasPropertyBitSetForFlags(property, unsignedTypeFlags[i])) {
return unsignedDataTypes[i];
}
}
} else {
for (size_t i = 0; i < numTypes; ++i) {
if (hasPropertyBitSetForFlags(property, dataTypeFlags[i])) {
return dataTypes[i];
}
}
}
return sd::DataType::UNKNOWN;
}
SD_HOST SD_INLINE void validateFlags(sd::LongType property, const sd::LongType flags[], size_t numFlags) {
LongType *flagIndices = new LongType[numFlags];
int numFlagsSet = 0;
for (size_t i = 0; i < numFlags; ++i) {
if (hasPropertyBitSetForFlags(property, flags[i])) {
flagIndices[i] = 1;
numFlagsSet++;
} else {
flagIndices[i] = 0;
}
}
if (numFlagsSet > 1) {
std::ostringstream errorMsg;
errorMsg << "Multiple data types are set for the given property: ";
for (size_t i = 0; i < numFlags; i++) {
if(flagIndices[i] == 1) {
errorMsg << "Flag index " << i << " (flag value: " << flags[i] << "), ";
}
}
errorMsg << "Total: " << numFlagsSet << " data types set.";
THROW_EXCEPTION(errorMsg.str().c_str());
}
delete[] flagIndices;
}
SD_HOST SD_INLINE void ArrayOptions::validateSingleDataType(sd::LongType property) {
const sd::LongType dataTypeFlags[] = DATA_TYPE_FLAGS;
const size_t numDataTypeFlags = sizeof(dataTypeFlags) / sizeof(sd::LongType);
validateFlags(property, dataTypeFlags, numDataTypeFlags);
if (hasPropertyBitSetForFlags(property, ARRAY_UNSIGNED)) {
const sd::LongType unsignedTypeFlags[] = ARRAY_UNSIGNED_TYPES;
const size_t numUnsignedTypeFlags = sizeof(unsignedTypeFlags) / sizeof(sd::LongType);
validateFlags(property, unsignedTypeFlags, numUnsignedTypeFlags);
}
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::numDataTypesSet(sd::LongType property) {
const sd::LongType dataTypeFlags[] = DATA_TYPE_FLAGS;
const size_t numDataTypeFlags = sizeof(dataTypeFlags) / sizeof(sd::LongType);
sd::LongType numFlagsSet = 0;
for (size_t i = 0; i < numDataTypeFlags; ++i) {
if (hasPropertyBitSetForFlags(property, dataTypeFlags[i])) {
numFlagsSet++;
}
}
return numFlagsSet;
}
SD_HOST SD_INLINE sd::DataType ArrayOptions::dataType(const sd::LongType *shapeInfo) {
// Return safe default instead of throwing - prevents SIGABRT crashes
if(shapeInfo == nullptr)
return DataType::FLOAT32; // Safe default for uninitialized arrays
auto extra = ArrayOptions::extra(shapeInfo);
return ArrayOptions::dataTypeValue(extra);
}
SD_HOST SD_INLINE SpaceType ArrayOptions::spaceTypeForFlags(const sd::LongType& flagStorage) {
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_QUANTIZED)) return SpaceType::QUANTIZED;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_COMPLEX)) return SpaceType::COMPLEX;
return SpaceType::CONTINUOUS; // by default we return continuous type here
}
SD_HOST SD_INLINE ArrayType ArrayOptions::arrayTypeForFlags(const sd::LongType& flagStorage) {
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_SPARSE)) return ArrayType::SPARSE;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_COMPRESSED)) return ArrayType::COMPRESSED;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_EMPTY)) return ArrayType::EMPTY;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_RAGGED)) return ArrayType::RAGGED;
return ArrayType::DENSE; // by default we return DENSE type here
}
SD_HOST SD_INLINE bool ArrayOptions::togglePropertyBitForFlags(sd::LongType& flagStorage, LongType property) {
flagStorage ^= property;
return hasPropertyBitSetForFlags(flagStorage, property);
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::unsetPropertyBitForFlags(sd::LongType& flagStorage, LongType property) {
return flagStorage & ~property;
}
SD_HOST SD_INLINE SparseType ArrayOptions::sparseTypeForFlags(const sd::LongType& flagStorage) {
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_CSC)) return SparseType::CSC;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_CSR)) return SparseType::CSR;
if (hasPropertyBitSetForFlags(flagStorage, ARRAY_COO)) return SparseType::COO;
return SparseType::LIL;
}
// Existing function that works with shapeInfo:
SD_HOST_DEVICE SD_INLINE SpaceType ArrayOptions::spaceType(const sd::LongType *shapeInfo) {
return spaceTypeForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE ArrayType ArrayOptions::arrayType(const sd::LongType *shapeInfo) {
return arrayTypeForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE ArrayType ArrayOptions::arrayType(sd::LongType *shapeInfo) {
return arrayTypeForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE bool ArrayOptions::isEmpty(sd::LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, EMPTY);
}
SD_HOST SD_INLINE bool ArrayOptions::arrayNeedsCopy(LongType *shapeInfo) {
return hasPropertyBitSetForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)], ARRAY_NEEDS_COPY);
}
SD_HOST SD_INLINE void ArrayOptions::toggleArrayNeedsCopy(LongType *shapeInfo) {
togglePropertyBit(shapeInfo, ARRAY_NEEDS_COPY);
}
SD_HOST SD_INLINE void ArrayOptions::toggleIsEmpty(sd::LongType *shapeInfo) {
togglePropertyBit(shapeInfo, EMPTY);
}
SD_HOST SD_INLINE bool ArrayOptions::isView(sd::LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, ARRAY_IS_VIEW);
}
SD_HOST SD_INLINE void ArrayOptions::toggleIsView(sd::LongType *shapeInfo) {
togglePropertyBit(shapeInfo, ARRAY_IS_VIEW);
}
SD_HOST SD_INLINE bool ArrayOptions::togglePropertyBit(sd::LongType *shapeInfo, LongType property) {
return togglePropertyBitForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)], property);
}
SD_HOST SD_INLINE void ArrayOptions::setPropertyBit(sd::LongType *shapeInfo, LongType property) {
shapeInfo[ArrayOptions::extraIndex(shapeInfo)] = setPropertyBitForFlagsValue(shapeInfo[ArrayOptions::extraIndex(shapeInfo)], property);
}
SD_HOST SD_INLINE void ArrayOptions::unsetPropertyBit(sd::LongType *shapeInfo, LongType property) {
shapeInfo[ArrayOptions::extraIndex(shapeInfo)] = unsetPropertyBitForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)], property);
}
SD_HOST SD_INLINE SparseType ArrayOptions::sparseType(const sd::LongType *shapeInfo) {
return sparseTypeForFlags(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
SD_HOST SD_INLINE void ArrayOptions::setPropertyBits(sd::LongType *shapeInfo, std::initializer_list<LongType> properties) {
for (auto v : properties) {
if (!hasPropertyBitSet(shapeInfo, v)) setPropertyBit(shapeInfo, v);
}
}
SD_HOST SD_INLINE void ArrayOptions::flagAsPaddedBuffer(sd::LongType *shapeInfo) {
if (!isNewFormat(shapeInfo)) return;
return setPropertyBit(shapeInfo, ARRAY_HAS_PADDED_BUFFER);
}
SD_HOST SD_INLINE bool ArrayOptions::hasPaddedBuffer(const sd::LongType *shapeInfo) {
if (!isNewFormat(shapeInfo)) return false;
return hasPropertyBitSet(shapeInfo, ARRAY_HAS_PADDED_BUFFER);
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::propertyWithoutDataTypeValue(sd::LongType extra) {
sd::LongType property = extra;
property = property & (~ARRAY_BOOL);
property = property & (~ARRAY_HALF);
property = property & (~ARRAY_BHALF);
property = property & (~ARRAY_FLOAT);
property = property & (~ARRAY_DOUBLE);
property = property & (~ARRAY_INT);
property = property & (~ARRAY_LONG);
property = property & (~ARRAY_CHAR);
property = property & (~ARRAY_SHORT);
property = property & (~ARRAY_UNSIGNED);
return property;
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::propertyWithoutDataType(const sd::LongType *shapeInfo) {
auto newCast = const_cast<sd::LongType *>(shapeInfo);
sd::LongType property = extra(newCast);
return propertyWithoutDataTypeValue(property);
}
SD_HOST SD_INLINE void ArrayOptions::resetDataType(sd::LongType *shapeInfo) {
setExtra(shapeInfo, propertyWithoutDataType(shapeInfo));
}
SD_HOST SD_INLINE LongType ArrayOptions::flagForDataType(const sd::DataType dataType) {
switch (dataType) {
case sd::DataType::BOOL:
return ARRAY_BOOL;
case sd::DataType::HALF:
return ARRAY_HALF;
case sd::DataType::BFLOAT16:
return ARRAY_BHALF;
case sd::DataType::FLOAT32:
return ARRAY_FLOAT;
case sd::DataType::DOUBLE:
return ARRAY_DOUBLE;
case sd::DataType::INT8:
return ARRAY_CHAR;
case sd::DataType::INT16:
return ARRAY_SHORT;
case sd::DataType::INT32:
return ARRAY_INT;
case sd::DataType::INT64:
return ARRAY_LONG;
case sd::DataType::UINT8:
return ARRAY_CHAR | ARRAY_UNSIGNED;
case sd::DataType::UINT16:
return ARRAY_SHORT | ARRAY_UNSIGNED;
case sd::DataType::UINT32 :
return ARRAY_INT | ARRAY_UNSIGNED;
case sd::DataType::UINT64 :
return ARRAY_LONG | ARRAY_UNSIGNED;
case sd::DataType::UTF8:
return ARRAY_UTF8;
case sd::DataType::UTF16:
return ARRAY_UTF16;
case sd::DataType::UTF32:
return ARRAY_UTF32;
default:
#ifndef __CUDA_ARCH__
std::string errorMessage;
errorMessage += "Can't set unknown data type ArrayOptions: ";
errorMessage += sd::DataTypeUtils::asString(dataType);
THROW_EXCEPTION(errorMessage.c_str());
return 0; // Never reached, but satisfies compiler warning
#else
printf("Can't set unknown data type");
return 0; // Return sentinel value for unknown types in CUDA code
#endif
}
}
SD_HOST SD_INLINE void ArrayOptions::setDataType(sd::LongType *shapeInfo, const sd::DataType dataType) {
switch (dataType) {
case sd::DataType::BOOL:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_BOOL);
break;
case sd::DataType::HALF:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_HALF);
break;
case sd::DataType::BFLOAT16:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_BHALF);
break;
case sd::DataType::FLOAT32:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_FLOAT);
break;
case sd::DataType::DOUBLE:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_DOUBLE);
break;
case sd::DataType::INT8:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_CHAR);
break;
case sd::DataType::INT16:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_SHORT);
break;
case sd::DataType::INT32:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_INT);
break;
case sd::DataType::INT64:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_LONG);
break;
case sd::DataType::UINT8:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_CHAR | ARRAY_UNSIGNED);
break;
case sd::DataType::UINT16:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_SHORT | ARRAY_UNSIGNED);
break;
case sd::DataType::UINT32:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_INT | ARRAY_UNSIGNED);
break;
case sd::DataType::UINT64:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_LONG | ARRAY_UNSIGNED);
break;
case sd::DataType::UTF8:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_UTF8);
break;
case sd::DataType::UTF16:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_UTF16);
break;
case sd::DataType::UTF32:
ArrayOptions::resetDataType(shapeInfo);
setPropertyBit(shapeInfo, ARRAY_UTF32);
break;
default:
#ifndef __CUDA_ARCH__
std::string errorMessage;
errorMessage += "Can't set unknown data type: ";
errorMessage += DataTypeUtils::asString(dataType);
THROW_EXCEPTION(errorMessage.c_str());
#else
printf("Can't set unknown data type");
#endif
validateSingleDataType(shapeInfo[ArrayOptions::extraIndex(shapeInfo)]);
}
#ifndef __CUDA_ARCH__
if(ArrayOptions::dataType(shapeInfo) != dataType) {
std::string errorMessage;
errorMessage += "setDataType: Data type set was not correct one. Expected ";
errorMessage += DataTypeUtils::asString(dataType);
errorMessage += " but got ";
errorMessage += sd::DataTypeUtils::asString(dataType);
THROW_EXCEPTION(errorMessage.c_str());
}
#else
printf("setDataType: Data type set was incorrect.");
#endif
}
SD_HOST SD_INLINE sd::LongType ArrayOptions::setDataTypeValue(sd::LongType extraStorage, const sd::DataType dataType) {
sd::LongType ret = extraStorage;
if (dataType == sd::DataType::UINT8
|| dataType == sd::DataType::UINT16
|| dataType == sd::DataType::UINT32 ||
dataType == sd::DataType::UINT64) {
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_UNSIGNED);
extraStorage = ret;
return extraStorage;
}
switch (dataType) {
case sd::DataType::BOOL:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_BOOL);
break;
case sd::DataType::HALF:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_HALF);
break;
case sd::DataType::BFLOAT16:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_BHALF);
break;
case sd::DataType::FLOAT32:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_FLOAT);
break;
case sd::DataType::DOUBLE:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_DOUBLE);
break;
case sd::DataType::INT8:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_CHAR);
break;
case sd::DataType::INT16:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_SHORT);
break;
case sd::DataType::INT32:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_INT);
break;
case sd::DataType::INT64:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_LONG);
break;
case sd::DataType::UINT8:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_CHAR);
break;
case sd::DataType::UINT16:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_SHORT);
break;
case sd::DataType::UINT32:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_INT);
break;
case sd::DataType::UINT64:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_LONG);
break;
case sd::DataType::UTF8:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_UTF8);
break;
case sd::DataType::UTF16:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_UTF16);
break;
case sd::DataType::UTF32:
ret = setPropertyBitForFlagsValue(extraStorage, ARRAY_UTF32);
break;
default:
#ifndef __CUDA_ARCH__
THROW_EXCEPTION("Can't set unknown data type");
#else
printf("Can't set unknown data type");
#endif
}
return ret;
}
SD_HOST sd::LongType ArrayOptions::defaultFlag() {
return DEFAULT_FLAG;
}
SD_HOST void ArrayOptions::resetFlags(sd::LongType *to) {
to[ArrayOptions::extraIndex(to)] = 1;
}
////////////////////////////////////////////////////////////////////////////////
SD_HOST SD_INLINE void ArrayOptions::copyDataType(sd::LongType *to, const sd::LongType *from) {
if(to == nullptr)
THROW_EXCEPTION("To Shape info was null!");
if(from == nullptr)
THROW_EXCEPTION("From Shape info was null!");
DataType dt = dataType(from);
sd::LongType flagForDt = flagForDataType(dt);
if(hasPropertyBitSet(to, flagForDt))
return;
setDataType(to, dt);
}
SD_HOST sd::LongType ArrayOptions::setPropertyBitForFlagsValue(LongType extraStorage, LongType property) {
if(hasPropertyBitSet(extraStorage, property))
return extraStorage;
return extraStorage | property;
}
// New method implementations to add to ArrayOptions.hXX
// needsCopy - const overload to check if array needs copy
SD_HOST SD_INLINE bool ArrayOptions::needsCopy(const LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, ARRAY_NEEDS_COPY);
}
// hasCopyOffset - check if specific input index has copy offset flag set
SD_HOST SD_INLINE bool ArrayOptions::hasCopyOffset(const LongType *shapeInfo, int inputIndex) {
if (inputIndex < 0 || inputIndex > 10) {
return false;
}
LongType flag = copyOffsetFlagForInput(inputIndex);
return hasPropertyBitSet(shapeInfo, flag);
}
// toggleCopyOffset - toggle copy offset flag for specific input index
SD_HOST SD_INLINE void ArrayOptions::toggleCopyOffset(LongType *shapeInfo, int inputIndex) {
if (inputIndex < 0 || inputIndex > 10) {
#ifndef __CUDA_ARCH__
THROW_EXCEPTION("Input index out of range [0-10]");
#else
printf("Input index out of range [0-10]\n");
#endif
return;
}
LongType flag = copyOffsetFlagForInput(inputIndex);
togglePropertyBit(shapeInfo, flag);
}
// copyOffsetFlagForInput - get the flag constant for a specific input index
SD_HOST SD_INLINE LongType ArrayOptions::copyOffsetFlagForInput(int inputIndex) {
switch (inputIndex) {
case 0: return ARRAY_COPY_OFFSET_INPUT_0;
case 1: return ARRAY_COPY_OFFSET_INPUT_1;
case 2: return ARRAY_COPY_OFFSET_INPUT_2;
case 3: return ARRAY_COPY_OFFSET_INPUT_3;
case 4: return ARRAY_COPY_OFFSET_INPUT_4;
case 5: return ARRAY_COPY_OFFSET_INPUT_5;
case 6: return ARRAY_COPY_OFFSET_INPUT_6;
case 7: return ARRAY_COPY_OFFSET_INPUT_7;
case 8: return ARRAY_COPY_OFFSET_INPUT_8;
case 9: return ARRAY_COPY_OFFSET_INPUT_9;
case 10: return ARRAY_COPY_OFFSET_INPUT_10;
default:
#ifndef __CUDA_ARCH__
THROW_EXCEPTION("Invalid input index for copy offset flag");
#else
printf("Invalid input index for copy offset flag\n");
#endif
return 0;
}
}
// clearAllCopyOffsets - clear all 11 copy offset flags at once
SD_HOST SD_INLINE void ArrayOptions::clearAllCopyOffsets(LongType *shapeInfo) {
for (int i = 0; i <= 10; i++) {
LongType flag = copyOffsetFlagForInput(i);
if (hasPropertyBitSet(shapeInfo, flag)) {
unsetPropertyBit(shapeInfo, flag);
}
}
}
// getActiveCopyOffsets - return count or bitmask of which inputs have offset flags set
SD_HOST SD_INLINE int ArrayOptions::getActiveCopyOffsets(const LongType *shapeInfo) {
int count = 0;
for (int i = 0; i <= 10; i++) {
LongType flag = copyOffsetFlagForInput(i);
if (hasPropertyBitSet(shapeInfo, flag)) {
count++;
}
}
return count;
}
// isView - const overload for consistency
SD_HOST SD_INLINE bool ArrayOptions::isView(const LongType *shapeInfo) {
return hasPropertyBitSet(shapeInfo, ARRAY_IS_VIEW);
}
}
#endif
+36
View File
@@ -0,0 +1,36 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef ND4J_ARRAY_TYPE_H
#define ND4J_ARRAY_TYPE_H
namespace sd {
enum ArrayType {
DENSE = 1,
SPARSE = 2,
COMPRESSED = 3,
EMPTY = 4,
RAGGED = 5,
};
}
#endif
+33
View File
@@ -0,0 +1,33 @@
/* ******************************************************************************
*
*
* 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.
//
#ifndef LIBND4J_BYTEORDER_H
#define LIBND4J_BYTEORDER_H
namespace sd {
enum ByteOrder {
LE = 0,
BE = 1,
};
}
#endif // LIBND4J_BYTEORDER_H
+36
View File
@@ -0,0 +1,36 @@
/* ******************************************************************************
*
*
* 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.
//
#ifndef LIBND4J_BYTEORDERUTILS_H
#define LIBND4J_BYTEORDERUTILS_H
#include <array/ByteOrder.h>
#include <graph/generated/array_generated.h>
#include <system/common.h>
namespace sd {
class SD_LIB_EXPORT ByteOrderUtils {
public:
static ByteOrder fromFlatByteOrder(graph::ByteOrder order);
};
} // namespace sd
#endif // LIBND4J_BYTEORDERUTILS_H
@@ -0,0 +1,64 @@
/* ******************************************************************************
*
*
* 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
#ifndef LIBND4J_CONSTANTDATABUFFER_H
#define LIBND4J_CONSTANTDATABUFFER_H
#include <array/DataType.h>
#include <array/PointerWrapper.h>
#include <system/common.h>
#include <memory>
namespace sd {
class SD_LIB_EXPORT ConstantDataBuffer {
private:
std::shared_ptr<PointerWrapper> _primaryBuffer;
std::shared_ptr<PointerWrapper> _specialBuffer = nullptr;
uint64_t _length = 0;
uint8_t _sizeOf = 0;
public:
ConstantDataBuffer(const std::shared_ptr<PointerWrapper> &primary, uint64_t numEelements, DataType dype);
ConstantDataBuffer(const std::shared_ptr<PointerWrapper> &primary, const std::shared_ptr<PointerWrapper> &special,
uint64_t numEelements, DataType dype);
ConstantDataBuffer(const ConstantDataBuffer &other);
ConstantDataBuffer() = default;
~ConstantDataBuffer() = default;
uint8_t sizeOf() const;
uint64_t length() const;
void *primary() const;
void *special() const;
ConstantDataBuffer &operator=(const ConstantDataBuffer &other) = default;
ConstantDataBuffer &operator=(ConstantDataBuffer &&other) noexcept = default;
template <typename T>
T *primaryAsT() const;
template <typename T>
T *specialAsT() const;
};
} // namespace sd
#endif // DEV_TESTS_CONSTANTDATABUFFER_H
@@ -0,0 +1,77 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef DEV_TESTS_CONSTANTDESCRIPTOR_H
#define DEV_TESTS_CONSTANTDESCRIPTOR_H
#include <array/ConstantDataBuffer.h>
#include <array/DataType.h>
#include <system/common.h>
#include <unordered_map>
#include <vector>
namespace sd {
class SD_LIB_EXPORT ConstantDescriptor {
private:
std::vector<LongType> _integerValues;
std::vector<double> _floatValues;
public:
ConstantDescriptor(double *values, int length);
ConstantDescriptor(LongType const *values, int length);
ConstantDescriptor(std::initializer_list<double> values);
explicit ConstantDescriptor(std::vector<LongType> &values);
explicit ConstantDescriptor(std::vector<double> &values);
~ConstantDescriptor() = default;
// equal to operator
bool operator==(const ConstantDescriptor &other) const;
// less than operator
bool operator<(const ConstantDescriptor &other) const;
bool isInteger() const;
bool isFloat() const;
LongType length() const;
const std::vector<LongType> &integerValues() const;
const std::vector<double> &floatValues() const;
};
} // namespace sd
#ifndef __JAVACPP_HACK__
namespace std {
template <>
class SD_LIB_EXPORT hash<sd::ConstantDescriptor> {
public:
size_t operator()(const sd::ConstantDescriptor &k) const;
};
} // namespace std
#endif
#endif // DEV_TESTS_CONSTANTDESCRIPTOR_H
+68
View File
@@ -0,0 +1,68 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef LIBND4J_CONSTANTHOLDER_H
#define LIBND4J_CONSTANTHOLDER_H
#include <array/ConstantDataBuffer.h>
#include <array/ConstantDescriptor.h>
#include <map>
#include <mutex>
namespace sd {
class ConstantHolder {
private:
int _deviceId = 0;
std::mutex _mutex;
std::map<DataType, ConstantDataBuffer> _buffers;
public:
ConstantHolder(const ConstantHolder &other);
ConstantHolder() = default;
~ConstantHolder() = default;
ConstantHolder &operator=(const ConstantHolder &other) = delete;
ConstantHolder &operator=(ConstantHolder &&other) = delete;
bool hasBuffer(DataType dataType);
template <typename T>
bool hasBuffer();
void addBuffer(ConstantDataBuffer &pointer, DataType dataType);
template <typename T>
void addBuffer(ConstantDataBuffer &pointer);
ConstantDataBuffer *getConstantDataBuffer(DataType dataType);
template <typename T>
ConstantDataBuffer *getConstantDataBuffer();
std::mutex *mutex();
};
} // namespace sd
#endif // DEV_TESTS_CONSTANTHOLDER_H
@@ -0,0 +1,57 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_ARRAY_CONSTANTOFFSETSBUFFER_H_
#define SD_ARRAY_CONSTANTOFFSETSBUFFER_H_
#include <array/PointerWrapper.h>
#include <system/common.h>
#include <memory>
namespace sd {
class SD_LIB_EXPORT ConstantOffsetsBuffer {
private:
static constexpr uint32_t MAGIC_VALID = 0xC0FFE575; // "COFFSETS" - magic number for validity check
uint32_t _magic = 0; // Validity marker to detect use-after-free
std::shared_ptr<PointerWrapper> _primaryOffsets;
std::shared_ptr<PointerWrapper> _specialOffsets;
public:
ConstantOffsetsBuffer(const std::shared_ptr<PointerWrapper> &primary);
ConstantOffsetsBuffer(const std::shared_ptr<PointerWrapper> &primary, const std::shared_ptr<PointerWrapper> &special);
ConstantOffsetsBuffer(); // Default constructor - sets magic number
~ConstantOffsetsBuffer(); // Need custom destructor to clear magic
inline bool isValid() const { return _magic == MAGIC_VALID; }
LongType *primary();
LongType *special();
LongType *platform();
};
} // namespace sd
#endif // SD_ARRAY_CONSTANTOFFSETSBUFFER_H_
@@ -0,0 +1,98 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_ARRAY_CONSTANTSHAPEBUFFER_H_
#define SD_ARRAY_CONSTANTSHAPEBUFFER_H_
#include <array/PointerWrapper.h>
#include <system/common.h>
#include <atomic>
#include <memory>
#include <string>
#ifndef __JAVACPP_HACK__
#if defined(SD_GCC_FUNCTRACE)
#include <exceptions/backward.hpp>
#endif
#endif
namespace sd {
class SD_LIB_EXPORT ConstantShapeBuffer {
private:
static constexpr uint32_t MAGIC_VALID = 0x58A9EB0F; // Magic number for validity check
uint32_t _magic; // Validity marker to detect use-after-free/garbage pointers
PointerWrapper* _primaryShapeInfo;
PointerWrapper* _specialShapeInfo;
std::atomic<int> _refCount;
public:
ConstantShapeBuffer( PointerWrapper* primary);
ConstantShapeBuffer( PointerWrapper* primary, PointerWrapper* special);
ConstantShapeBuffer();
~ConstantShapeBuffer();
/**
* Check if this buffer is valid (not garbage/use-after-free).
* Uses magic number validation.
*/
inline bool isValid() const { return _magic == MAGIC_VALID; }
#ifndef __JAVACPP_HACK__
#if defined(SD_GCC_FUNCTRACE)
backward::StackTrace st;
#endif
#endif
LongType *primary() ;
LongType *special() ;
LongType *platform() ;
/**
* Get the stack trace as a formatted string.
* Returns empty string if functrace is not enabled.
*/
std::string getStackTraceAsString() const;
/**
* Manual reference counting for safe cross-JNI ownership.
* Increments reference count - call when handing out a pointer.
*/
void addRef();
/**
* Manual reference counting for safe cross-JNI ownership.
* Decrements reference count and deletes when reaching zero.
* Call when done with a pointer (e.g., in deleteConstantShapeBuffer).
*/
void release();
/**
* Get current reference count (for debugging).
*/
int getRefCount() const;
};
} // namespace sd
#endif // SD_ARRAY_CONSTANTSHAPEBUFFER_H_
@@ -0,0 +1,41 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_CUDAYPOINTERDEALLOCATOR_H_
#define SD_CUDAYPOINTERDEALLOCATOR_H_
#include <array/PointerDeallocator.h>
#include <system/common.h>
namespace sd {
class SD_LIB_EXPORT CudaPointerDeallocator : public PointerDeallocator {
public:
CudaPointerDeallocator() = default;
~CudaPointerDeallocator() = default;
void release(void *ptr) override;
};
} // namespace sd
#endif // SD_CUDAYPOINTERDEALLOCATOR_H_
+226
View File
@@ -0,0 +1,226 @@
/* ******************************************************************************
*
*
* 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)
//
#ifndef DEV_TESTS_DATABUFFER_H
#define DEV_TESTS_DATABUFFER_H
#include <array/DataType.h>
#include <execution/LaunchContext.h>
#include <memory/Workspace.h>
#include <system/common.h>
#include <system/op_boilerplate.h>
#include <cstring>
#include <mutex>
namespace sd {
class SD_LIB_EXPORT DataBuffer {
private:
// Magic number for validity checking (pattern from DirectShapeTrie validation)
// Set in constructor, cleared in destructor, checked before use
// Helps detect use-after-free and corrupted pointers
static constexpr uint32_t MAGIC_NUMBER = 0xDA7ABF01; // "DA7ABF01" (DataBuffer v01)
uint32_t _magicNumber = MAGIC_NUMBER;
void *_primaryBuffer = nullptr;
void *_specialBuffer = nullptr;
LongType _lenInBytes = 0;
memory::Workspace *_workspace = nullptr;
std::atomic<int> _deviceId;
std::mutex _deleteMutex;
#ifndef __JAVACPP_HACK__
#if defined(SD_CUDA)
mutable std::atomic<LongType> _counter;
mutable std::atomic<LongType> _writePrimary;
mutable std::atomic<LongType> _writeSpecial;
mutable std::atomic<LongType> _readPrimary;
mutable std::atomic<LongType> _readSpecial;
#endif
#if defined(SD_GCC_FUNCTRACE)
StackTrace *allocationStackTracePrimary = nullptr;
StackTrace *allocationStackTraceSpecial = nullptr;
StackTrace *creationStackTrace = nullptr;
#endif
#endif
bool closed = false;
// Helper template function for printing host buffer content (implementation in .cpp)
template <typename T>
void printHostBufferContent(void* buffer, sd::LongType offset, sd::LongType length);
void setCountersToZero();
void copyCounters(const DataBuffer &other);
void deleteSpecial();
void deletePrimary();
void deleteBuffers();
void setAllocFlags(const bool isOwnerPrimary, const bool isOwnerSpecial = false);
void allocateBuffers(const bool allocBoth = false);
void setSpecial(void *special, const bool isOwnerSpecial);
void copyBufferFromHost(const void *hostBuffer, size_t sizeToCopyinBytes = 0, const LongType offsetThis = 0,
const LongType offsetHostBuffer = 0);
public:
bool _isOwnerPrimary;
bool _isOwnerSpecial;
bool isConstant = false;
DataType _dataType;
DataBuffer(void *primary, void *special, const size_t lenInBytes, const DataType dataType,
const bool isOwnerPrimary = false, const bool isOwnerSpecial = false,
memory::Workspace *workspace = nullptr);
DataBuffer(void *primary, const size_t lenInBytes, const DataType dataType, const bool isOwnerPrimary = false,
memory::Workspace *workspace = nullptr);
DataBuffer(const void *hostBuffer, // copies data from hostBuffer to own memory buffer
const DataType dataType, const size_t lenInBytes, memory::Workspace *workspace = nullptr);
DataBuffer(const sd::LongType lenInBytes, const DataType dataType, memory::Workspace *workspace = nullptr,
const bool allocBoth = false);
DataBuffer(const DataBuffer &other);
DataBuffer(DataBuffer &&other);
explicit DataBuffer();
~DataBuffer();
DataBuffer &operator=(const DataBuffer &other);
DataBuffer &operator=(DataBuffer &&other) noexcept;
DataType getDataType();
void setDataType(DataType dataType);
size_t getLenInBytes() const;
size_t getNumElements();
template <typename T>
void *primaryAtOffset(const LongType offset);
template <typename T>
void *specialAtOffset(const LongType offset);
void *primary();
void *special();
void printAllocationTrace();
/**
* Validate that this DataBuffer object is in a sane state.
* Following DirectShapeTrie validation pattern: check magic number, closed flag, etc.
* Throws exception with detailed message if validation fails.
* Call this before accessing any member in methods that might be called
* on dangling/corrupted pointers (like special(), primary(), etc.)
*/
void validateIntegrity() const;
void allocatePrimary();
void allocateSpecial();
void writePrimary() const;
void writeSpecial() const;
void readPrimary() const;
void readSpecial() const;
bool isPrimaryActual() const;
bool isSpecialActual() const;
void expand(const uint64_t size);
int deviceId() const;
void setDeviceId(int deviceId);
void migrate();
template <typename T>
SD_INLINE T *primaryAsT();
template <typename T>
SD_INLINE T *specialAsT();
void markConstant(bool reallyConstant);
void syncToPrimary(const LaunchContext *context, const bool forceSync = false);
void syncToSpecial(const bool forceSync = false);
void setToZeroBuffers(const bool both = false);
void copyBufferFrom(const DataBuffer &other, size_t sizeToCopyinBytes = 0, const LongType offsetThis = 0,
const LongType offsetOther = 0);
void setPrimaryBuffer(void *buffer, size_t length);
void setSpecialBuffer(void *buffer, size_t length);
void showBufferLimited();
//for Debug purposes
void showCounters(const char* msg1, const char* msg2);
/**
* This method deletes buffers, if we're owners
*/
void close();
bool isClosed() { return closed; }
void printPrimaryAllocationStackTraces();
void printSpecialAllocationTraces();
DataBuffer dup();
/**
* Helper method to format creation stack trace as string for error messages.
* Returns formatted stack trace if SD_GCC_FUNCTRACE is enabled, empty string otherwise.
*/
std::string getCreationTraceAsString() const;
void printHostDevice(long offset);
static void memcpy(DataBuffer *dst, DataBuffer *src, sd::LongType startingOffset, sd::LongType dstOffset);
/**
* Print detailed buffer information including host and device content if available
* @param msg - Optional message to display
* @param offset - Starting offset for printing buffer contents
* @param limit - Maximum number of elements to print
*/
#ifndef __JAVACPP_HACK__
void printBufferDebug(const char* msg = nullptr, sd::LongType offset = 0, sd::LongType limit = 10);
#endif
};
///// IMPLEMENTATION OF INLINE METHODS /////
////////////////////////////////////////////////////////////////////////
template <typename T>
T *DataBuffer::primaryAsT() {
return reinterpret_cast<T *>(_primaryBuffer);
}
////////////////////////////////////////////////////////////////////////
template <typename T>
T *DataBuffer::specialAsT() {
return reinterpret_cast<T *>(_specialBuffer);
}
} // namespace sd
#endif // DEV_TESTS_DATABUFFER_H
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef ND4J_DATATYPE_H
#define ND4J_DATATYPE_H
namespace sd {
enum DataType {
INHERIT = 0,
BOOL = 1,
FLOAT8 = 2,
HALF = 3,
HALF2 = 4,
FLOAT32 = 5,
DOUBLE = 6,
INT8 = 7,
INT16 = 8,
INT32 = 9,
INT64 = 10,
UINT8 = 11,
UINT16 = 12,
UINT32 = 13,
UINT64 = 14,
QINT8 = 15,
QINT16 = 16,
BFLOAT16 = 17,
UTF8 = 50,
UTF16 = 51,
UTF32 = 52,
ANY = 100,
AUTO = 200,
UNKNOWN = 255
};
}
#endif
+276
View File
@@ -0,0 +1,276 @@
/* ******************************************************************************
*
*
* 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.
//
#ifndef LIBND4J_DATATYPECONVERSIONS_H
#define LIBND4J_DATATYPECONVERSIONS_H
#include <array/DataType.h>
#include <execution/Threads.h>
#include <helpers/BitwiseUtils.h>
#include <helpers/logger.h>
#include <loops/type_conversions.h>
#include <system/common.h>
#include <system/op_boilerplate.h>
#include <types/float16.h>
namespace sd {
template <typename T>
class SD_LIB_EXPORT DataTypeConversions {
private:
template <typename T2>
static SD_INLINE void rconv(bool isBe, bool canKeep, T *buffer, LongType length, void *src) {
if (std::is_same<T, T2>::value && canKeep) {
// When the types match and we can keep the data as-is,
// copy the data from src to buffer.
ops::safe_copy(buffer, static_cast<const T*>(src), static_cast<size_t>(length));
} else {
// Otherwise, allocate a temporary array of type T2.
auto tmp = new T2[length];
ops::safe_copy(tmp, static_cast<const T2*>(src), static_cast<size_t>(length));
// If any conversion from T2 to T is required, perform it here.
// For example, if T can be constructed from T2, you might do:
// for (LongType i = 0; i < length; ++i) {
// buffer[i] = static_cast<T>(tmp[i]);
// }
// Free the temporary storage.
delete[] tmp;
}
}
public:
// The convertType function, modified to use safe_copy instead of direct memcpy.
// (Assumes that T, DataType, ByteOrder, LongType, BitwiseUtils, DataTypeConversions,
// TypeCast, PRAGMA_THREADS_FOR, samediff::Threads, sd_printf, THROW_EXCEPTION, etc.
// are defined elsewhere in your code base.)
static SD_INLINE void convertType(void *vbuffer, void *src, DataType dataType, ByteOrder order, LongType length) {
auto buffer = reinterpret_cast<T *>(vbuffer);
bool isBe = BitwiseUtils::isBE();
bool canKeep = (isBe && order == BE) || (!isBe && order == LE);
switch (dataType) {
#ifdef HAS_BOOL
case BOOL: {
DataTypeConversions<T>::template rconv<bool>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_UINT8
case UINT8: {
DataTypeConversions<T>::template rconv<uint8_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_INT8
case INT8: {
DataTypeConversions<T>::template rconv<int8_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_INT16
case INT16: {
DataTypeConversions<T>::template rconv<int16_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_INT32
case INT32: {
DataTypeConversions<T>::template rconv<int>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_LONG
case INT64: {
DataTypeConversions<T>::template rconv<LongType>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_FLOAT32
case FLOAT32: {
if (std::is_same<T, float>::value && canKeep) {
ops::safe_copy(buffer, static_cast<const float*>(src), static_cast<size_t>(length));
} else {
auto tmp = new float[length];
ops::safe_copy(tmp, static_cast<const float*>(src), static_cast<size_t>(length));
#if __GNUC__ <= 4
if (!canKeep) {
for (sd::LongType e = 0; e < length; e++)
buffer[e] = BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
} else {
TypeCast::convertGeneric<float, T>(nullptr, tmp, length, buffer);
}
#else
auto func = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++)
buffer[e] = canKeep
? static_cast<T>(tmp[e])
: BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
};
samediff::Threads::parallel_for(func, 0, length);
#endif
delete[] tmp;
}
} break;
#endif
#ifdef HAS_DOUBLE
case DOUBLE: {
if (std::is_same<T, double>::value && canKeep) {
ops::safe_copy(buffer, static_cast<const double*>(src), static_cast<size_t>(length));
} else {
auto tmp = new double[length];
ops::safe_copy(tmp, static_cast<const double*>(src), static_cast<size_t>(length));
#if __GNUC__ <= 4
if (!canKeep) {
for (sd::LongType e = 0; e < length; e++)
buffer[e] = BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
} else {
TypeCast::convertGeneric<double, T>(nullptr, tmp, length, buffer);
}
#else
auto func = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++)
buffer[e] = canKeep
? static_cast<T>(tmp[e])
: BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
};
samediff::Threads::parallel_for(func, 0, length);
#endif
delete[] tmp;
}
} break;
#endif
#ifdef HAS_FLOAT16
case HALF: {
if (std::is_same<T, float16>::value && canKeep) {
ops::safe_copy(buffer, static_cast<const float16*>(src), static_cast<size_t>(length));
} else {
auto tmp = new float16[length];
ops::safe_copy(tmp, static_cast<const float16*>(src), static_cast<size_t>(length));
#if __GNUC__ <= 4
if (!canKeep) {
for (sd::LongType e = 0; e < length; e++)
buffer[e] = BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
} else {
TypeCast::convertGeneric<float16, T>(nullptr, tmp, length, buffer);
}
#else
auto func = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++)
buffer[e] = canKeep
? static_cast<T>(tmp[e])
: BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
};
samediff::Threads::parallel_for(func, 0, length);
#endif
delete[] tmp;
}
} break;
#endif
#ifdef HAS_BFLOAT16
case BFLOAT16: {
if (std::is_same<T, bfloat16>::value && canKeep) {
ops::safe_copy(buffer, static_cast<const bfloat16*>(src), static_cast<size_t>(length));
} else {
auto tmp = new bfloat16[length];
ops::safe_copy(tmp, static_cast<const bfloat16*>(src), static_cast<size_t>(length));
#if __GNUC__ <= 4
if (!canKeep) {
for (sd::LongType e = 0; e < length; e++)
buffer[e] = BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
} else {
TypeCast::convertGeneric<bfloat16, T>(nullptr, tmp, length, buffer);
}
#else
auto func = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++)
buffer[e] = canKeep
? static_cast<T>(tmp[e])
: BitwiseUtils::swap_bytes<T>(static_cast<T>(tmp[e]));
};
samediff::Threads::parallel_for(func, 0, length);
#endif
delete[] tmp;
}
} break;
#endif
#ifdef HAS_UINT16
case UINT16: {
DataTypeConversions<T>::template rconv<uint16_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_UINT32
case UINT32: {
DataTypeConversions<T>::template rconv<uint32_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_UNSIGNEDLONG
case UINT64: {
DataTypeConversions<T>::template rconv<uint64_t>(isBe, canKeep, buffer, length, src);
} break;
#endif
#ifdef HAS_UTF8
case UTF8: {
// UTF8 handling would go here if needed
sd_print("UTF8 DataType conversion not implemented\n");
THROW_EXCEPTION("UTF8 DataType conversion not implemented");
} break;
#endif
#ifdef HAS_UTF16
case UTF16: {
// UTF16 handling would go here if needed
sd_print("UTF16 DataType conversion not implemented\n");
THROW_EXCEPTION("UTF16 DataType conversion not implemented");
} break;
#endif
#ifdef HAS_UTF32
case UTF32: {
// UTF32 handling would go here if needed
sd_print("UTF32 DataType conversion not implemented\n");
THROW_EXCEPTION("UTF32 DataType conversion not implemented");
} break;
#endif
default: {
sd_printf("Unsupported DataType requested: [%i]\n", static_cast<int>(dataType));
THROW_EXCEPTION("Unsupported DataType");
}
}
}
};
} // namespace sd
#endif // LIBND4J_DATATYPECONVERSIONS_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,394 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// Implementation of robust data type validation and error reporting
//
#include <array/DataType.h>
#include <array/DataTypeValidation.h>
#include <exceptions/allocation_exception.h>
#include <system/op_enums.h>
#include <sstream>
#include "system/op_boilerplate.h"
#include <helpers/logger.h>
namespace sd {
// Static member definitions
std::unordered_set<DataType> DataTypeValidation::compiledTypes_;
std::unordered_map<DataType, std::string> DataTypeValidation::typeNames_;
std::unordered_map<int, DataType> DataTypeValidation::enumMapping_;
bool DataTypeValidation::initialized_ = false;
void DataTypeValidation::initialize() {
if (initialized_) return;
populateTypeNames();
populateCompiledTypes();
// Build enum ID to DataType mapping
for (const auto& pair : typeNames_) {
enumMapping_[static_cast<int>(pair.first)] = pair.first;
}
initialized_ = true;
}
void DataTypeValidation::initializeIfNeeded() {
if (!initialized_) {
initialize();
}
}
void DataTypeValidation::populateTypeNames() {
if (initialized_) return;
// All possible data types defined in DataType enum
typeNames_[DataType::INHERIT] = "INHERIT";
typeNames_[DataType::BOOL] = "BOOL";
typeNames_[DataType::FLOAT8] = "FLOAT8";
typeNames_[DataType::HALF] = "HALF";
typeNames_[DataType::HALF2] = "HALF2";
typeNames_[DataType::FLOAT32] = "FLOAT32";
typeNames_[DataType::DOUBLE] = "DOUBLE";
typeNames_[DataType::INT8] = "INT8";
typeNames_[DataType::INT16] = "INT16";
typeNames_[DataType::INT32] = "INT32";
typeNames_[DataType::INT64] = "INT64";
typeNames_[DataType::UINT8] = "UINT8";
typeNames_[DataType::UINT16] = "UINT16";
typeNames_[DataType::UINT32] = "UINT32";
typeNames_[DataType::UINT64] = "UINT64";
typeNames_[DataType::QINT8] = "QINT8";
typeNames_[DataType::QINT16] = "QINT16";
typeNames_[DataType::BFLOAT16] = "BFLOAT16";
typeNames_[DataType::UTF8] = "UTF8";
typeNames_[DataType::UTF16] = "UTF16";
typeNames_[DataType::UTF32] = "UTF32";
typeNames_[DataType::UNKNOWN] = "UNKNOWN";
}
void DataTypeValidation::populateCompiledTypes() {
if (initialized_) return;
// Auto-register types based on compile-time flags
// This mirrors the logic from the CMake type system
// Boolean type
#if defined(HAS_BOOL)
registerCompiledType(DataType::BOOL, "BOOL");
#endif
// Floating point types - check both CMake-generated and alias defines
#if defined(HAS_FLOAT) || defined(HAS_FLOAT32)
registerCompiledType(DataType::FLOAT32, "FLOAT32");
#endif
#if defined(HAS_DOUBLE) || defined(HAS_FLOAT64)
registerCompiledType(DataType::DOUBLE, "DOUBLE");
#endif
#if defined(HAS_FLOAT16) || defined(HAS_HALF)
registerCompiledType(DataType::HALF, "HALF");
#endif
#if defined(HAS_BFLOAT16) || defined(HAS_BFLOAT)
registerCompiledType(DataType::BFLOAT16, "BFLOAT16");
#endif
// Integer types - check both normalized (_T) and alias forms
#if defined(HAS_INT8_T) || defined(HAS_INT8)
registerCompiledType(DataType::INT8, "INT8");
#endif
#if defined(HAS_INT16_T) || defined(HAS_INT16)
registerCompiledType(DataType::INT16, "INT16");
#endif
#if defined(HAS_INT32_T) || defined(HAS_INT32) || defined(HAS_INT)
registerCompiledType(DataType::INT32, "INT32");
#endif
#if defined(HAS_INT64_T) || defined(HAS_INT64) || defined(HAS_LONG)
registerCompiledType(DataType::INT64, "INT64");
#endif
// Unsigned integer types
#if defined(HAS_UINT8_T) || defined(HAS_UINT8)
registerCompiledType(DataType::UINT8, "UINT8");
#endif
#if defined(HAS_UINT16_T) || defined(HAS_UINT16)
registerCompiledType(DataType::UINT16, "UINT16");
#endif
#if defined(HAS_UINT32_T) || defined(HAS_UINT32)
registerCompiledType(DataType::UINT32, "UINT32");
#endif
#if defined(HAS_UINT64_T) || defined(HAS_UINT64) || defined(HAS_UNSIGNEDLONG)
registerCompiledType(DataType::UINT64, "UINT64");
#endif
// String types - only if string operations are enabled
#if defined(SD_ENABLE_STRING_OPERATIONS)
#if defined(HAS_STD_STRING) || defined(HAS_UTF8)
registerCompiledType(DataType::UTF8, "UTF8");
#endif
#if defined(HAS_STD_U16STRING) || defined(HAS_UTF16)
registerCompiledType(DataType::UTF16, "UTF16");
#endif
#if defined(HAS_STD_U32STRING) || defined(HAS_UTF32)
registerCompiledType(DataType::UTF32, "UTF32");
#endif
#endif
}
void DataTypeValidation::registerCompiledType(DataType type, const std::string& name) {
compiledTypes_.insert(type);
typeNames_[type] = name;
}
bool DataTypeValidation::isValidDataType(DataType dataType) {
initializeIfNeeded();
return typeNames_.find(dataType) != typeNames_.end();
}
bool DataTypeValidation::isValidDataType(int dataTypeId) {
initializeIfNeeded();
return enumMapping_.find(dataTypeId) != enumMapping_.end();
}
std::vector<DataType> DataTypeValidation::getAvailableDataTypes() {
initializeIfNeeded();
std::vector<DataType> result;
result.reserve(compiledTypes_.size());
for (const auto& type : compiledTypes_) {
result.push_back(type);
}
return result;
}
bool DataTypeValidation::isCompiledDataType(DataType dataType) {
initializeIfNeeded();
return compiledTypes_.find(dataType) != compiledTypes_.end();
}
std::string DataTypeValidation::getDataTypeErrorMessage(int dataTypeId, const char* context) {
initializeIfNeeded();
std::ostringstream oss;
if (!isValidDataType(dataTypeId)) {
oss << "Invalid data type ID: " << dataTypeId << " (not a valid enum value)";
} else {
DataType dt = enumMapping_[dataTypeId];
const char* typeName = getDataTypeName(dt);
if (!isCompiledDataType(dt)) {
oss << "Data type " << typeName << " (ID: " << dataTypeId
<< ") is not available in this build";
} else {
oss << "Unexpected error with data type " << typeName
<< " (ID: " << dataTypeId << ") - type is valid and compiled";
}
}
if (context) {
oss << "\nContext: " << context;
}
// Add build configuration info with better diagnosis
#if defined(SD_SELECTIVE_TYPES)
oss << "\nBuild configuration: Selective types enabled";
// List the define flags that would enable missing types
if (isValidDataType(dataTypeId)) {
DataType dt = enumMapping_[dataTypeId];
if (!isCompiledDataType(dt)) {
const char* typeName = getDataTypeName(dt);
oss << "\nTo enable this type, rebuild with the type included in SD_TYPES_LIST";
oss << "\nExample: cmake -DSD_TYPES_LIST=\"float32;int32;int64;" << typeName << "\" ..";
}
}
#else
oss << "\nBuild configuration: All types enabled (SD_SELECTIVE_TYPES not defined)";
oss << "\nThis error suggests a build system configuration issue.";
#endif
// Add available types
auto availableTypes = getAvailableDataTypes();
if (!availableTypes.empty()) {
oss << "\nAvailable types in this build (" << availableTypes.size() << "): ";
for (size_t i = 0; i < availableTypes.size(); ++i) {
if (i > 0) oss << ", ";
oss << getDataTypeName(availableTypes[i]);
}
} else {
oss << "\nCRITICAL: No data types are available in this build!";
oss << "\nThis indicates a serious build configuration problem.";
// Debug information to help diagnose the issue
oss << "\nDiagnostic information:";
oss << "\n Total possible types: " << typeNames_.size();
oss << "\n Compiled types: " << compiledTypes_.size();
// Show which defines are actually set
oss << "\n Active defines:";
#if defined(HAS_BOOL)
oss << " HAS_BOOL";
#endif
#if defined(HAS_FLOAT)
oss << " HAS_FLOAT";
#endif
#if defined(HAS_FLOAT32)
oss << " HAS_FLOAT32";
#endif
#if defined(HAS_DOUBLE)
oss << " HAS_DOUBLE";
#endif
#if defined(HAS_INT32_T)
oss << " HAS_INT32_T";
#endif
#if defined(HAS_INT32)
oss << " HAS_INT32";
#endif
#if defined(HAS_INT)
oss << " HAS_INT";
#endif
#if defined(HAS_INT64_T)
oss << " HAS_INT64_T";
#endif
#if defined(HAS_LONG)
oss << " HAS_LONG";
#endif
#if defined(SD_SELECTIVE_TYPES)
oss << " SD_SELECTIVE_TYPES";
#endif
if (compiledTypes_.empty()) {
oss << "\n No type defines matched - check CMake type generation";
}
}
// Add suggestions based on the situation
if (!isValidDataType(dataTypeId)) {
oss << "\nSuggestion: Check NDArray creation and data type initialization";
} else if (availableTypes.empty()) {
oss << "\nSuggestion: Check CMake build configuration and type define generation";
oss << "\nVerify that setup_type_definitions() was called in CMake";
} else {
oss << "\nSuggestion: Use one of the available types or rebuild with the required type enabled";
}
return oss.str();
}
const char* DataTypeValidation::getDataTypeName(DataType dataType) {
initializeIfNeeded();
auto it = typeNames_.find(dataType);
return (it != typeNames_.end()) ? it->second.c_str() : "UNKNOWN";
}
std::string DataTypeValidation::getBuildInfo() {
initializeIfNeeded();
std::ostringstream oss;
oss << "=== DATA TYPE VALIDATION BUILD INFO ===\n";
#if defined(SD_SELECTIVE_TYPES)
oss << "Build type: Selective types (SD_SELECTIVE_TYPES defined)\n";
#else
oss << "Build type: All types (SD_SELECTIVE_TYPES not defined)\n";
#endif
oss << "Total possible types: " << typeNames_.size() << "\n";
oss << "Compiled types: " << compiledTypes_.size() << "\n";
if (!compiledTypes_.empty()) {
oss << "Available types: ";
auto types = getAvailableDataTypes();
for (size_t i = 0; i < types.size(); ++i) {
if (i > 0) oss << ", ";
oss << getDataTypeName(types[i]);
}
oss << "\n";
} else {
oss << "CRITICAL: No types compiled!\n";
oss << "This indicates a build configuration error.\n";
// Show active defines for debugging
oss << "Active defines:";
#if defined(HAS_BOOL)
oss << " HAS_BOOL";
#endif
#if defined(HAS_FLOAT)
oss << " HAS_FLOAT";
#endif
#if defined(HAS_FLOAT32)
oss << " HAS_FLOAT32";
#endif
#if defined(HAS_DOUBLE)
oss << " HAS_DOUBLE";
#endif
#if defined(HAS_INT32_T)
oss << " HAS_INT32_T";
#endif
#if defined(HAS_INT32)
oss << " HAS_INT32";
#endif
#if defined(HAS_INT)
oss << " HAS_INT";
#endif
#if defined(HAS_INT64_T)
oss << " HAS_INT64_T";
#endif
#if defined(HAS_LONG)
oss << " HAS_LONG";
#endif
if (compiledTypes_.empty()) {
oss << "\nNo defines matched - check CMake setup_type_definitions()";
}
}
oss << "==========================================\n";
return oss.str();
}
// DataTypeValidator implementation
void DataTypeValidator::validateOrThrow() {
if (!DataTypeValidation::isValidDataType(dataType_)) {
std::string errorMsg = DataTypeValidation::getDataTypeErrorMessage(
static_cast<int>(dataType_), context_.c_str());
THROW_EXCEPTION(errorMsg.c_str());
}
if (!DataTypeValidation::isCompiledDataType(dataType_)) {
std::string errorMsg = DataTypeValidation::getDataTypeErrorMessage(
dataType_, context_.c_str());
THROW_EXCEPTION(errorMsg.c_str());
}
}
} // namespace sd
+156
View File
@@ -0,0 +1,156 @@
/* ******************************************************************************
*
*
* 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 for robust data type validation and error reporting
//
#ifndef ND4J_DATATYPE_VALIDATION_H
#define ND4J_DATATYPE_VALIDATION_H
#include <array/DataType.h>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <system/common.h>
namespace sd {
/**
* Comprehensive data type validation and error reporting system
*
* This class provides robust validation for data types, distinguishing between:
* - Invalid data type IDs (not in enum)
* - Valid but uncompiled data types (selective builds)
* - Runtime vs compile-time mismatches
*/
class SD_LIB_EXPORT DataTypeValidation {
private:
static std::unordered_set<DataType> compiledTypes_;
static std::unordered_map<DataType, std::string> typeNames_;
static std::unordered_map<int, DataType> enumMapping_;
static bool initialized_;
static void initializeIfNeeded();
static void populateTypeNames();
static void populateCompiledTypes();
public:
/**
* Initialize the validation system (called automatically)
*/
static void initialize();
/**
* Check if a data type enum value is valid
* @param dataType The data type to check
* @return true if the data type exists in the enum
*/
static bool isValidDataType(DataType dataType);
/**
* Check if a numeric data type ID is valid
* @param dataTypeId The numeric data type ID to check
* @return true if the ID maps to a valid enum value
*/
static bool isValidDataType(int dataTypeId);
/**
* Check if a data type is compiled into this build
* @param dataType The data type to check
* @return true if the type is available in this binary
*/
static bool isCompiledDataType(DataType dataType);
/**
* Get a descriptive error message for numeric data type ID
* @param dataTypeId The problematic data type ID
* @param context Context string (function name, etc.)
* @return Detailed error message with suggestions
*/
static std::string getDataTypeErrorMessage(int dataTypeId, const char* context);
/**
* Get list of available data types in this build
* @return Vector of compiled data types
*/
static std::vector<DataType> getAvailableDataTypes();
/**
* Get human-readable name for data type
* @param dataType The data type
* @return String name (e.g., "FLOAT32", "INT64")
*/
static const char* getDataTypeName(DataType dataType);
/**
* Get build configuration information
* @return String describing the build configuration and available types
*/
static std::string getBuildInfo();
/**
* Register a compiled data type (used internally by build system)
* @param type The data type to register
* @param name Human-readable name
*/
static void registerCompiledType(DataType type, const std::string& name);
};
/**
* RAII helper for data type validation in critical paths
*
* Usage:
* DataTypeValidator validator(myDataType, "MyFunction::myMethod()");
* // Will throw if data type is invalid or not compiled
*/
class SD_LIB_EXPORT DataTypeValidator {
private:
DataType dataType_;
std::string context_;
public:
/**
* Constructor that validates the data type
* @param dt Data type to validate
* @param context Context for error messages
* @throws std::exception if validation fails
*/
DataTypeValidator(DataType dt, const std::string& context)
: dataType_(dt), context_(context) {
validateOrThrow();
}
/**
* Perform validation and throw if invalid
* @throws std::exception with detailed error message
*/
void validateOrThrow();
/**
* Get the validated data type
* @return The data type (guaranteed to be valid and compiled)
*/
DataType getDataType() const { return dataType_; }
};
}
#endif // ND4J_DATATYPE_VALIDATION_H
@@ -0,0 +1,169 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// DeallocatorServiceLifecycleTracker - Tracks DeallocatorService statistics
// from the Java side for memory leak detection.
// Only active when SD_GCC_FUNCTRACE is defined.
//
#ifndef LIBND4J_DEALLOCATORSERVICELIFECYCLETRACKER_H
#define LIBND4J_DEALLOCATORSERVICELIFECYCLETRACKER_H
#include <cstddef>
#include <atomic>
#include <system/common.h>
namespace sd {
namespace analysis {
class ComprehensiveLeakAnalyzer;
}
namespace array {
/**
* DeallocatorServiceLifecycleTracker - Singleton class for tracking DeallocatorService
* statistics from the Java side for memory leak detection.
*
* This receives snapshot data from Java's DeallocatorService and tracks
* memory management patterns.
*/
class DeallocatorServiceLifecycleTracker {
friend class sd::analysis::ComprehensiveLeakAnalyzer;
public:
/**
* Get singleton instance
*/
static DeallocatorServiceLifecycleTracker& getInstance() {
static DeallocatorServiceLifecycleTracker instance;
return instance;
}
/**
* Enable tracking
*/
void enable() {
_enabled.store(true);
}
/**
* Disable tracking
*/
void disable() {
_enabled.store(false);
}
/**
* Check if tracking is enabled
*/
bool isEnabled() const {
return _enabled.load();
}
/**
* Record a snapshot from Java DeallocatorService
* @param totalAllocations Total allocations count
* @param totalDeallocations Total deallocations count
* @param totalBytesAllocated Total bytes allocated
* @param totalBytesDeallocated Total bytes deallocated
* @param peakLiveCount Peak live object count
* @param peakBytes Peak bytes in use
*/
void recordSnapshot(LongType totalAllocations, LongType totalDeallocations,
LongType totalBytesAllocated, LongType totalBytesDeallocated,
LongType peakLiveCount, LongType peakBytes) {
if (!_enabled.load()) return;
_totalAllocations.store(totalAllocations);
_totalDeallocations.store(totalDeallocations);
_totalBytesAllocated.store(totalBytesAllocated);
_totalBytesDeallocated.store(totalBytesDeallocated);
_peakLiveCount.store(peakLiveCount);
_peakBytes.store(peakBytes);
}
/**
* Get current live count (allocations - deallocations)
*/
LongType getCurrentLiveCount() const {
return _totalAllocations.load() - _totalDeallocations.load();
}
/**
* Get current bytes in use
*/
LongType getCurrentBytesInUse() const {
return _totalBytesAllocated.load() - _totalBytesDeallocated.load();
}
/**
* Get total allocations
*/
LongType getTotalAllocations() const {
return _totalAllocations.load();
}
/**
* Get total deallocations
*/
LongType getTotalDeallocations() const {
return _totalDeallocations.load();
}
/**
* Get peak live count
*/
LongType getPeakLiveCount() const {
return _peakLiveCount.load();
}
/**
* Get peak bytes
*/
LongType getPeakBytes() const {
return _peakBytes.load();
}
private:
DeallocatorServiceLifecycleTracker() : _enabled(false),
_totalAllocations(0), _totalDeallocations(0),
_totalBytesAllocated(0), _totalBytesDeallocated(0),
_peakLiveCount(0), _peakBytes(0) {}
~DeallocatorServiceLifecycleTracker() = default;
// Disable copy and move
DeallocatorServiceLifecycleTracker(const DeallocatorServiceLifecycleTracker&) = delete;
DeallocatorServiceLifecycleTracker& operator=(const DeallocatorServiceLifecycleTracker&) = delete;
DeallocatorServiceLifecycleTracker(DeallocatorServiceLifecycleTracker&&) = delete;
DeallocatorServiceLifecycleTracker& operator=(DeallocatorServiceLifecycleTracker&&) = delete;
std::atomic<bool> _enabled;
std::atomic<LongType> _totalAllocations;
std::atomic<LongType> _totalDeallocations;
std::atomic<LongType> _totalBytesAllocated;
std::atomic<LongType> _totalBytesDeallocated;
std::atomic<LongType> _peakLiveCount;
std::atomic<LongType> _peakBytes;
};
} // namespace array
} // namespace sd
#endif // LIBND4J_DEALLOCATORSERVICELIFECYCLETRACKER_H
+66
View File
@@ -0,0 +1,66 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef DEV_TESTS_EXTRAARGUMENTS_H
#define DEV_TESTS_EXTRAARGUMENTS_H
#include <array/DataType.h>
#include <stdlib.h>
#include <system/common.h>
#include <initializer_list>
#include <vector>
namespace sd {
class SD_LIB_EXPORT ExtraArguments {
private:
std::vector<double> _fpArgs;
std::vector<LongType> _intArgs;
std::vector<Pointer> _pointers;
template <typename T>
void convertAndCopy(Pointer pointer, LongType offset);
void *allocate(size_t length, size_t elementSize);
public:
explicit ExtraArguments(std::initializer_list<double> arguments);
explicit ExtraArguments(std::initializer_list<LongType> arguments);
explicit ExtraArguments(const std::vector<double> &arguments);
explicit ExtraArguments(const std::vector<int> &arguments);
explicit ExtraArguments(const std::vector<LongType> &arguments);
explicit ExtraArguments();
~ExtraArguments();
template <typename T>
void *argumentsAsT(LongType offset = 0);
void *argumentsAsT(DataType dataType, LongType offset = 0);
size_t length();
};
} // namespace sd
#endif // DEV_TESTS_EXTRAARGUMENTS_H
+120
View File
@@ -0,0 +1,120 @@
/* ******************************************************************************
*
*
* 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/DataBuffer.h>
#include <array/DataType.h>
#include <system/common.h>
#include <memory>
#ifndef LIBND4J_INTEROPDATABUFFER_H
#define LIBND4J_INTEROPDATABUFFER_H
namespace sd {
/**
* This class is a wrapper for DataBuffer, suitable for sharing DataBuffer between front-end and back-end languages
*/
class SD_LIB_EXPORT InteropDataBuffer {
private:
DataBuffer *_dataBuffer = nullptr;
bool owner;
DataType _dataType = DataType::UNKNOWN;
public:
size_t _cachedLenInBytes = 0;
void* _cachedPrimaryPtr = nullptr; // Cached for deallocation tracking
void* _cachedSpecialPtr = nullptr; // Cached for deallocation tracking
bool _closed = false;
bool isConstant = false;
InteropDataBuffer(InteropDataBuffer *dataBuffer, uint64_t length);
InteropDataBuffer(DataBuffer * databuffer);
InteropDataBuffer(size_t lenInBytes, DataType dtype, bool allocateBoth);
~InteropDataBuffer() {
// Mark as closed FIRST to prevent any concurrent access
_closed = true;
// If we own the DataBuffer and it's not constant, clean it up
if (owner && !isConstant && _dataBuffer != nullptr) {
delete _dataBuffer;
}
// Always null out the pointer to prevent use-after-free
_dataBuffer = nullptr;
}
#ifndef __JAVACPP_HACK__
DataBuffer * getDataBuffer() const;
DataBuffer * dataBuffer();
// In InteropDataBuffer class, add these public methods:
// Check if the underlying DataBuffer pointer is still valid
// by checking if it's non-null
bool hasValidDataBuffer() const { return _dataBuffer != nullptr; }
// Get the DataBuffer pointer directly without validation
DataBuffer* getDataBufferDirect() const { return _dataBuffer; }
// Mark the DataBuffer as invalid (set to null)
void invalidateDataBuffer() { _dataBuffer = nullptr; }
bool isOwner() const { return owner; }
// Safe accessor that doesn't touch other fields
DataBuffer* getDataBufferUnsafe() const {
return _dataBuffer;
}
#endif
void printDbAllocationTrace();
void *primary() const;
void *special() const;
void markConstant(bool reallyConstant) {
isConstant = reallyConstant;
dataBuffer()->markConstant(reallyConstant);
}
void setPrimary(void *ptr, size_t length);
void setSpecial(void *ptr, size_t length);
void expand(size_t newlength);
int deviceId() const;
void setDeviceId(int deviceId);
//updates whether the buffer is the owner of its associated buffers or not.
void markOwner(bool owner);
int useCount() const;
static void registerSpecialUse(const std::vector<const InteropDataBuffer *> &writeList,
const std::vector<const InteropDataBuffer *> &readList);
static void prepareSpecialUse(const std::vector<const InteropDataBuffer *> &writeList,
const std::vector<const InteropDataBuffer *> &readList,
bool synchronizeWritables = false);
static void registerPrimaryUse(const std::vector<const InteropDataBuffer *> &writeList,
const std::vector<const InteropDataBuffer *> &readList);
static void preparePrimaryUse(const std::vector<const InteropDataBuffer *> &writeList,
const std::vector<const InteropDataBuffer *> &readList,
bool synchronizeWritables = false);
};
} // namespace sd
#endif // LIBND4J_INTEROPDATABUFFER_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+226
View File
@@ -0,0 +1,226 @@
/*
* ******************************************************************************
* *
* *
* * 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 2018-09-16.
// @author Oleg Semeniv <oleg.semeniv@gmail.com>
// @author Abdelrauf
#ifndef DEV_TESTS_NDARRAYFACTORY_H
#define DEV_TESTS_NDARRAYFACTORY_H
#include <array/NDArray.h>
#include <initializer_list>
#include <vector>
#include <execution/LaunchContext.h>
#include <string>
namespace sd {
class SD_LIB_EXPORT NDArrayFactory {
private:
template <typename T>
static void memcpyFromVector(void *ptr, const std::vector<T> &vector);
public:
template <typename T>
static NDArray *empty_(LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *empty_(DataType dataType, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray empty(LaunchContext *context = LaunchContext ::defaultContext());
static NDArray empty(DataType dataType, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *valueOf(const std::initializer_list<LongType> &shape, T value, char order = 'c',
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *valueOf(std::vector<LongType> &shape, T value, const char order = 'c',
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *valueOf(std::vector<LongType> &shape, NDArray&value, const char order = 'c',
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *linspace(T from, T to, LongType numElements);
static NDArray create(ShapeDescriptor *shapeDescriptor, LaunchContext *context = LaunchContext ::defaultContext());
static NDArray create(DataType dtype, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *create_(const T value, LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *create_(DataType dtype, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray create(const T value, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray create(DataType type, const T scalar, LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *vector(LongType length, T startingValue = (T)0,
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *create_(const char order, std::vector<LongType> &shape,
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *create_(const char order, std::vector<LongType> &shape, DataType dataType,
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray *create_(char order, const std::vector<LongType> &shape, const std::vector<T> &data,
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray create(char order, const std::vector<LongType> &shape, const std::vector<T> &data,
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray create(char order, const std::vector<LongType> &shape,
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray create(char order, const std::vector<LongType> &shape, DataType dtype,
LaunchContext *context = LaunchContext ::defaultContext());
template <typename T>
static NDArray create(const std::vector<T> &values, LaunchContext *context = LaunchContext ::defaultContext());
#ifndef __JAVACPP_HACK__
// this method only available out of javacpp
template <typename T>
static NDArray create(T *buffer, char order, const std::initializer_list<LongType> &shape,
LaunchContext *context = LaunchContext ::defaultContext());
/**
* This method creates NDArray from .npy file
* @param fileName
* @return
*/
static NDArray fromNpyFile(const char *fileName);
#if defined(HAS_UTF8)
/**
* This factory create array from utf8 string
* @return NDArray default dataType UTF8
*/
static NDArray string(const char *string, DataType dtype = UTF8,
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *string_(const char *string, DataType dtype = UTF8,
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *string_(const std::string &string, DataType dtype = UTF8,
LaunchContext *context = LaunchContext ::defaultContext());
static NDArray string(const std::string &string, DataType dtype = UTF8,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::vector<const char *> &strings,
DataType dataType = UTF8, LaunchContext *context = LaunchContext ::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::vector<std::string> &string,
DataType dataType = UTF8, LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<const char *> &strings,
DataType dataType = UTF8, LaunchContext *context = LaunchContext ::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<std::string> &string,
DataType dataType = UTF8, LaunchContext *context = LaunchContext ::defaultContext());
#endif
#if defined(HAS_UTF16)
/**
* This factory create array from utf16 string
* @return NDArray default dataType UTF16
*/
static NDArray string(const char16_t *u16string, DataType dtype = UTF16,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(const char16_t *u16string, DataType dtype = UTF16,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(const std::u16string &u16string, DataType dtype = UTF16,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(const std::u16string &u16string, DataType dtype = UTF16,
LaunchContext *context = LaunchContext::defaultContext());
/**
* This factory create array from vector of utf16 strings
* @return NDArray default dataType UTF16
*/
static NDArray string(std::vector<LongType> &shape, const std::initializer_list<const char16_t *> &strings,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::initializer_list<std::u16string> &string,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::vector<const char16_t *> &strings,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::vector<std::u16string> &string,
DataType dtype = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape,
const std::initializer_list<const char16_t *> &strings,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::initializer_list<std::u16string> &string,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<const char16_t *> &strings,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<std::u16string> &string,
DataType dataType = UTF16, LaunchContext *context = LaunchContext::defaultContext());
#endif
#if defined(HAS_UTF32)
/**
* This factory create array from utf32 string
* @return NDArray default dataType UTF32
*/
static NDArray string(const char32_t *u32string, DataType dtype = UTF32,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(const char32_t *u32string, DataType dtype = UTF32,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(const std::u32string &u32string, DataType dtype = UTF32,
LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(const std::u32string &u32string, DataType dtype = UTF32,
LaunchContext *context = LaunchContext::defaultContext());
/**
* This factory create array from vector of utf32 strings
* @return NDArray default dataType UTF32
*/
static NDArray string(std::vector<LongType> &shape, const std::initializer_list<const char32_t *> &strings,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::initializer_list<std::u32string> &string,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray string(std::vector<LongType> &shape, const std::vector<const char32_t *> &strings, DataType dtype,
LaunchContext *context);
static NDArray string(std::vector<LongType> &shape, const std::vector<std::u32string> &string,
DataType dtype = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape,
const std::initializer_list<const char32_t *> &strings,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::initializer_list<std::u32string> &string,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<const char32_t *> &strings,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
static NDArray *string_(std::vector<LongType> &shape, const std::vector<std::u32string> &string,
DataType dataType = UTF32, LaunchContext *context = LaunchContext::defaultContext());
#endif
#endif
};
} // namespace sd
#endif // DEV_TESTS_NDARRAYFACTORY_H
+769
View File
@@ -0,0 +1,769 @@
/*
* ******************************************************************************
* *
* *
* * 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
* *****************************************************************************
*/
/**
* Streamlined implementation of NDArray operators
*/
#include <array/NDArray.h>
#include <array/NDArrayFactory.h>
#include <legacy/NativeOpExecutioner.h>
#include <system/Environment.h>
namespace sd {
/////////////////////////////////////////////////////
// Scalar-Array Operators - Helper Functions
/////////////////////////////////////////////////////
SD_LIB_EXPORT NDArray* operator+(NDArray& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator+(NDArray&& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator+(NDArray& arr1, NDArray&& arr2);
SD_LIB_EXPORT NDArray* operator+(NDArray&& arr1, NDArray&& arr2);
// Subtraction: NDArray - NDArray
SD_LIB_EXPORT NDArray* operator-(NDArray& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator-(NDArray&& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator-(NDArray& arr1, NDArray&& arr2);
SD_LIB_EXPORT NDArray* operator-(NDArray&& arr1, NDArray&& arr2);
// Multiplication: NDArray * NDArray
SD_LIB_EXPORT NDArray* operator*(NDArray& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator*(NDArray&& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator*(NDArray& arr1, NDArray&& arr2);
SD_LIB_EXPORT NDArray* operator*(NDArray&& arr1, NDArray&& arr2);
// Division: NDArray / NDArray
SD_LIB_EXPORT NDArray* operator/(NDArray& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator/(NDArray&& arr1, NDArray& arr2);
SD_LIB_EXPORT NDArray* operator/(NDArray& arr1, NDArray&& arr2);
SD_LIB_EXPORT NDArray* operator/(NDArray&& arr1, NDArray&& arr2);
template <typename T>
NDArray* scalarArrayOpHelper(NDArray* arr, T scalar, sd::scalar::Ops op, bool inPlace) {
if (arr->isS()) {
std::string errorMessage = "scalarArrayOpHelper: you can't use this method on String array!";
THROW_EXCEPTION(errorMessage.c_str());
}
auto tmp = NDArrayFactory::create(arr->dataType(), scalar, arr->getContext());
if (inPlace && !arr->isView()) {
NDArray::prepareSpecialUse({arr}, {arr, tmp});
NativeOpExecutioner::execScalar(arr->getContext(), op,
arr->buffer(), arr->shapeInfo(), arr->specialBuffer(), arr->specialShapeInfo(),
arr->buffer(), arr->shapeInfo(), arr->specialBuffer(), arr->specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({arr}, {arr, tmp});
delete tmp;
return arr;
} else {
NDArray *result = new NDArray(arr->shapeInfo(), DataTypeUtils::pickPairwiseResultType(arr->dataType(), DataTypeUtils::fromT<T>()),
false, arr->getContext());
NDArray::prepareSpecialUse({result}, {arr, tmp});
NativeOpExecutioner::execScalar(arr->getContext(), op,
arr->buffer(), arr->shapeInfo(), arr->specialBuffer(), arr->specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {arr, tmp});
delete tmp;
return result;
}
}
/////////////////////////////////////////////////////
// Array-Array Operators - Helper Function
/////////////////////////////////////////////////////
NDArray* arrayArrayOpHelper(NDArray* arr1, NDArray* arr2, sd::pairwise::Ops op, bool inPlace) {
if (arr1->isS() || arr2->isS()) {
std::string errorMessage = "arrayArrayOpHelper: you can't use this method on String arrays!";
THROW_EXCEPTION(errorMessage.c_str());
}
if (!Environment::getInstance().isExperimentalBuild() && arr1->dataType() != arr2->dataType() &&
(arr1->dataType() != DataType::BOOL || arr2->dataType() != BOOL)) {
std::string errorMessage;
errorMessage += "arrayArrayOpHelper: Cannot operate on arrays of different types: ";
errorMessage += DataTypeUtils::asString(arr1->dataType());
errorMessage += " and ";
errorMessage += DataTypeUtils::asString(arr2->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
if (arr1->lengthOf() == arr2->lengthOf() && arr1->rankOf() == arr2->rankOf()) {
NDArray* result = nullptr;
const bool canUseArr1 = inPlace && !arr1->isView();
const bool canUseArr2 = inPlace && !arr2->isView();
if (canUseArr1)
result = arr1;
else if (canUseArr2)
result = arr2;
else
result = new NDArray(arr1->shapeInfo(),
DataTypeUtils::pickPairwiseResultType(arr1->shapeInfo(), arr2->shapeInfo()),
false, arr1->getContext());
NDArray::prepareSpecialUse({result}, {arr1, arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1->getContext(), op,
arr1->buffer(), arr1->shapeInfo(), arr1->specialBuffer(), arr1->specialShapeInfo(),
arr2->buffer(), arr2->shapeInfo(), arr2->specialBuffer(), arr2->specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {arr1, arr2});
return result;
}
// Broadcast case
sd::BroadcastOpsTuple broadcastOp;
switch (op) {
case sd::pairwise::Add: broadcastOp = sd::BroadcastOpsTuple::Add(); break;
case sd::pairwise::Subtract: broadcastOp = sd::BroadcastOpsTuple::Subtract(); break;
case sd::pairwise::Multiply: broadcastOp = sd::BroadcastOpsTuple::Multiply(); break;
case sd::pairwise::Divide: broadcastOp = sd::BroadcastOpsTuple::Divide(); break;
default:
std::string errorMessage = "arrayArrayOpHelper: Unsupported pairwise operation for broadcasting";
THROW_EXCEPTION(errorMessage.c_str());
}
return arr1->applyTrueBroadcast(broadcastOp, arr2);
}
/////////////////////////////////////////////////////
// Addition Operators (NDArray + scalar)
/////////////////////////////////////////////////////
template <typename T, typename>
NDArray* operator+(NDArray& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Add, false);
}
template <typename T, typename>
NDArray* operator+(NDArray&& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Add, true);
}
template <typename T, typename>
NDArray* operator+(T scalar, NDArray& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Add, false);
}
template <typename T, typename>
NDArray* operator+(T scalar, NDArray&& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Add, true);
}
#define INSTANTIATE_SCALAR_OP(OP, T) \
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
GET_FIRST(T), \
template SD_LIB_EXPORT NDArray* operator OP<GET_SECOND(T), typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<GET_SECOND(T)>::value>::type>(NDArray& arr, GET_SECOND(T) scalar); \
template SD_LIB_EXPORT NDArray* operator OP<GET_SECOND(T), typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<GET_SECOND(T)>::value>::type>(NDArray&& arr, GET_SECOND(T) scalar); \
template SD_LIB_EXPORT NDArray* operator OP<GET_SECOND(T), typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<GET_SECOND(T)>::value>::type>(GET_SECOND(T) scalar, NDArray& arr); \
template SD_LIB_EXPORT NDArray* operator OP<GET_SECOND(T), typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<GET_SECOND(T)>::value>::type>(GET_SECOND(T) scalar, NDArray&& arr); \
))
#define INSTANTIATE_ALL_SCALAR_OPS(T) \
INSTANTIATE_SCALAR_OP(+, T) \
INSTANTIATE_SCALAR_OP(-, T) \
INSTANTIATE_SCALAR_OP(*, T) \
INSTANTIATE_SCALAR_OP(/, T)
ITERATE_LIST((SD_NUMERIC_TYPES), INSTANTIATE_ALL_SCALAR_OPS)
/////////////////////////////////////////////////////
// Subtraction Operators (NDArray - scalar)
/////////////////////////////////////////////////////
template <typename T, typename>
NDArray* operator-(NDArray& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Subtract, false);
}
template <typename T, typename>
NDArray* operator-(NDArray&& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Subtract, true);
}
template <typename T, typename>
NDArray* operator-(T scalar, NDArray& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::ReverseSubtract, false);
}
template <typename T, typename>
NDArray* operator-(T scalar, NDArray&& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::ReverseSubtract, true);
}
/////////////////////////////////////////////////////
// Multiplication Operators (NDArray * scalar)
/////////////////////////////////////////////////////
template <typename T, typename>
NDArray* operator*(NDArray& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Multiply, false);
}
template <typename T, typename>
NDArray* operator*(NDArray&& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Multiply, true);
}
template <typename T, typename>
NDArray* operator*(T scalar, NDArray& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Multiply, false);
}
template <typename T, typename>
NDArray* operator*(T scalar, NDArray&& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Multiply, true);
}
/////////////////////////////////////////////////////
// Division Operators (NDArray / scalar)
/////////////////////////////////////////////////////
template <typename T, typename>
NDArray* operator/(NDArray& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Divide, false);
}
template <typename T, typename>
NDArray* operator/(NDArray&& arr, T scalar) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::Divide, true);
}
template <typename T, typename>
NDArray* operator/(T scalar, NDArray& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::ReverseDivide, false);
}
template <typename T, typename>
NDArray* operator/(T scalar, NDArray&& arr) {
return scalarArrayOpHelper(&arr, scalar, sd::scalar::ReverseDivide, true);
}
/////////////////////////////////////////////////////
// Array-Array Operators
/////////////////////////////////////////////////////
// Addition: NDArray + NDArray
NDArray* operator+(NDArray& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Add, false);
}
NDArray* operator+(NDArray&& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Add, true);
}
NDArray* operator+(NDArray& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Add, true);
}
NDArray* operator+(NDArray&& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Add, true);
}
// Subtraction: NDArray - NDArray
NDArray* operator-(NDArray& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Subtract, false);
}
NDArray* operator-(NDArray&& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Subtract, true);
}
NDArray* operator-(NDArray& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Subtract, true);
}
NDArray* operator-(NDArray&& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Subtract, true);
}
// Multiplication: NDArray * NDArray
NDArray* operator*(NDArray& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Multiply, false);
}
NDArray* operator*(NDArray&& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Multiply, true);
}
NDArray* operator*(NDArray& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Multiply, true);
}
NDArray* operator*(NDArray&& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Multiply, true);
}
// Division: NDArray / NDArray
NDArray* operator/(NDArray& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Divide, false);
}
NDArray* operator/(NDArray&& arr1, NDArray& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Divide, true);
}
NDArray* operator/(NDArray& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Divide, true);
}
NDArray* operator/(NDArray&& arr1, NDArray&& arr2) {
return arrayArrayOpHelper(&arr1, &arr2, sd::pairwise::Divide, true);
}
/////////////////////////////////////////////////////
// Compound Assignment Operators
/////////////////////////////////////////////////////
// Direct implementation for compound assignment with scalar values
template <typename T, typename>
void NDArray::operator+=(const T scalar) {
if (isS()) {
std::string errorMessage = "NDArray::operator+=: you can't use this method on String array!";
THROW_EXCEPTION(errorMessage.c_str());
}
auto tmp = NDArrayFactory::create(dataType(), scalar, getContext());
NDArray::prepareSpecialUse({this}, {this, tmp});
NativeOpExecutioner::execScalar(getContext(), sd::scalar::Add,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({this}, {this, tmp});
delete tmp;
}
template <typename T, typename>
void NDArray::operator-=(const T scalar) {
if (isS()) {
std::string errorMessage = "NDArray::operator-=: you can't use this method on String array!";
THROW_EXCEPTION(errorMessage.c_str());
}
auto tmp = NDArrayFactory::create(dataType(), scalar, getContext());
NDArray::prepareSpecialUse({this}, {this, tmp});
NativeOpExecutioner::execScalar(getContext(), sd::scalar::Subtract,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({this}, {this, tmp});
delete tmp;
}
template <typename T, typename>
void NDArray::operator*=(const T scalar) {
if (isS()) {
std::string errorMessage = "NDArray::operator*=: you can't use this method on String array!";
THROW_EXCEPTION(errorMessage.c_str());
}
auto tmp = NDArrayFactory::create(dataType(), scalar, getContext());
NDArray::prepareSpecialUse({this}, {this, tmp});
NativeOpExecutioner::execScalar(getContext(), sd::scalar::Multiply,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({this}, {this, tmp});
delete tmp;
}
template <typename T, typename>
void NDArray::operator/=(const T scalar) {
if (isS()) {
std::string errorMessage = "NDArray::operator/=: you can't use this method on String array!";
THROW_EXCEPTION(errorMessage.c_str());
}
auto tmp = NDArrayFactory::create(dataType(), scalar, getContext());
NDArray::prepareSpecialUse({this}, {this, tmp});
NativeOpExecutioner::execScalar(getContext(), sd::scalar::Divide,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
tmp->buffer(), tmp->shapeInfo(), tmp->specialBuffer(), tmp->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({this}, {this, tmp});
delete tmp;
}
// Define instantiations matching the header file declarations exactly
#define INSTANTIATE_COMPOUND_ASSIGN_OP(OP, T) \
EVAL(SD_IF_SINGLE_ALIAS_COMPILED_DECL( \
GET_FIRST(T), \
template SD_LIB_EXPORT void NDArray::operator OP<GET_SECOND(T), typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<GET_SECOND(T)>::value>::type>(const GET_SECOND(T) scalar); \
))
#define INSTANTIATE_ALL_COMPOUND_ASSIGN_OPS(T) \
INSTANTIATE_COMPOUND_ASSIGN_OP(+=, T) \
INSTANTIATE_COMPOUND_ASSIGN_OP(-=, T) \
INSTANTIATE_COMPOUND_ASSIGN_OP(*=, T) \
INSTANTIATE_COMPOUND_ASSIGN_OP(/=, T)
ITERATE_LIST((SD_NUMERIC_TYPES), INSTANTIATE_ALL_COMPOUND_ASSIGN_OPS)
// Helper for compound assignment operators with arrays
void compoundAssignArray(NDArray& arr1, NDArray& arr2, sd::pairwise::Ops op, sd::BroadcastOpsTuple broadcastOp) {
if (arr1.isS() || arr2.isS()) {
std::string errorMessage = "compoundAssignArray: you can't use this method on String arrays!";
THROW_EXCEPTION(errorMessage.c_str());
}
if (!Environment::getInstance().isExperimentalBuild() && arr1.dataType() != arr2.dataType() &&
(arr1.dataType() != DataType::BOOL || arr2.dataType() != BOOL)) {
std::string errorMessage;
errorMessage += "compoundAssignArray: Cannot operate on arrays of different types: ";
errorMessage += DataTypeUtils::asString(arr1.dataType());
errorMessage += " and ";
errorMessage += DataTypeUtils::asString(arr2.dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
if (arr1.lengthOf() == arr2.lengthOf() && arr1.rankOf() == arr2.rankOf()) {
NDArray::prepareSpecialUse({&arr1}, {&arr1, &arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1.getContext(), op,
arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(), arr1.specialShapeInfo(),
arr2.buffer(), arr2.shapeInfo(), arr2.specialBuffer(), arr2.specialShapeInfo(),
arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(), arr1.specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({&arr1}, {&arr1, &arr2});
} else {
sd::LongType *bShape = nullptr;
if (!ShapeUtils::evalBroadcastShapeInfo(arr1.shapeInfo(), arr2.shapeInfo(), true, bShape, arr1.getContext()->getWorkspace())) {
std::string errorMessage = "compoundAssignArray: the shapes are not suitable for broadcast operation!";
THROW_EXCEPTION(errorMessage.c_str());
}
if (shape::equalsTypesAndShapesSoft(arr1.shapeInfo(), bShape)) {
arr1.applyTrueBroadcast(broadcastOp, &arr2, &arr1, false);
} else {
NDArray result(bShape, true, arr1.getContext());
arr1.applyTrueBroadcast(broadcastOp, &arr2, &result, false);
arr1 = std::move(result);
}
}
}
// Compound assignment with arrays
void NDArray::operator+=(NDArray& other) {
compoundAssignArray(*this, other, sd::pairwise::Add, sd::BroadcastOpsTuple::Add());
}
void NDArray::operator+=(NDArray&& other) {
operator+=(other);
}
void NDArray::operator-=(NDArray& other) {
compoundAssignArray(*this, other, sd::pairwise::Subtract, sd::BroadcastOpsTuple::Subtract());
}
void NDArray::operator-=(NDArray&& other) {
operator-=(other);
}
void NDArray::operator*=(NDArray& other) {
compoundAssignArray(*this, other, sd::pairwise::Multiply, sd::BroadcastOpsTuple::Multiply());
}
void NDArray::operator*=(NDArray&& other) {
operator*=(other);
}
void NDArray::operator/=(NDArray& other) {
compoundAssignArray(*this, other, sd::pairwise::Divide, sd::BroadcastOpsTuple::Divide());
}
void NDArray::operator/=(NDArray&& other) {
operator/=(other);
}
/////////////////////////////////////////////////////
// Pointer-Based Binary Operators (Forward Declarations)
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
// Pointer-Scalar Operator Templates (Declarations)
/////////////////////////////////////////////////////
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator+(NDArray* arr, T scalar);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator+(T scalar, NDArray* arr);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator-(NDArray* arr, T scalar);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator-(T scalar, NDArray* arr);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator*(NDArray* arr, T scalar);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator*(T scalar, NDArray* arr);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator/(NDArray* arr, T scalar);
template <typename T, typename = typename std::enable_if<DataTypeUtils::scalarTypesForNDarray<T>::value>::type>
SD_LIB_EXPORT NDArray* operator/(T scalar, NDArray* arr);
// Unary negative operator
NDArray NDArray::operator-() & {
NDArray result(shapeInfo(), false, getContext());
NDArray::prepareSpecialUse({&result}, {this});
NativeOpExecutioner::execTransformSame(getContext(), sd::transform::Neg,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
result.buffer(), result.shapeInfo(), result.specialBuffer(),
result.specialShapeInfo(), nullptr, nullptr, nullptr);
NDArray::registerSpecialUse({&result}, {this});
return result;
}
NDArray NDArray::operator-() && {
NDArray::prepareSpecialUse({this}, {this});
NativeOpExecutioner::execTransformSame(getContext(), sd::transform::Neg,
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
buffer(), shapeInfo(), specialBuffer(), specialShapeInfo(),
nullptr, nullptr, nullptr);
NDArray::registerSpecialUse({this}, {this});
return std::move(*this);
}
template <typename T1, typename T2, typename>
NDArray* operator-(T1 &&arr1, T2 &&arr2) {
if (arr1.isS() || arr2.isS())
THROW_EXCEPTION("operator-(T&& arr1, T&& arr2): you can't use this method on String arrays!");
if (!Environment::getInstance().isExperimentalBuild() && arr1.dataType() != arr2.dataType() &&
(arr1.dataType() != DataType::BOOL || arr2.dataType() != BOOL)) {
// Always throw exception - never use _exit(1) which kills the entire process
THROW_EXCEPTION(sd::datatype_exception::build("operator-(T&& arr1, T&& arr2): Cannot subtract different types",
arr1.dataType(), arr2.dataType()).what());
}
PointersManager pointersManager(arr1.getContext(), "operator-(T&& arr1, T&& arr2)");
if (arr1.lengthOf() == arr2.lengthOf() && arr1.rankOf() == arr2.rankOf()) {
const bool isArr1Rvalue = !std::is_reference<T1>::value && !arr1.isView();
const bool isArr2Rvalue = !std::is_reference<T2>::value && !arr2.isView();
NDArray *result = nullptr;
if (isArr1Rvalue)
result = const_cast<NDArray *>(&arr1);
else if (isArr2Rvalue)
result = const_cast<NDArray *>(&arr2);
else
result = new NDArray(arr1.shapeInfo(), DataTypeUtils::pickPairwiseResultType(arr1.shapeInfo(), arr2.shapeInfo()),
false, arr1.getContext());
NDArray::prepareSpecialUse({result}, {&arr1, &arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1.getContext(), sd::pairwise::Subtract, arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(),
arr1.specialShapeInfo(), arr2.buffer(), arr2.shapeInfo(), arr2.specialBuffer(), arr2.specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {&arr1, &arr2});
return result;
}
NDArray* arr1Addr = &arr1;
NDArray* arr2Addr = &arr2;
return std::forward<T1>(arr1).applyTrueBroadcast(sd::BroadcastOpsTuple::Subtract(), arr2Addr);
}
template SD_LIB_EXPORT NDArray* operator-<NDArray &, NDArray &, void>(NDArray &arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator-<NDArray &, NDArray, void>(NDArray &arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* operator-<NDArray, NDArray &, void>(NDArray &&arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator-<NDArray, NDArray, void>(NDArray &&arr1, NDArray &&arr2);
template <typename T1, typename T2, typename>
NDArray* operator*(T1 &&arr1, T2 &&arr2) {
if (arr1.isS() || arr2.isS()) {
THROW_EXCEPTION("operator*(T&& arr1, T&& arr2): you can't use this method on String arrays!");
}
if (!Environment::getInstance().isExperimentalBuild() && arr1.dataType() != arr2.dataType() &&
(arr1.dataType() != DataType::BOOL || arr2.dataType() != BOOL)) {
std::string errorMessage;
errorMessage += "operator*(T&& arr1, T&& arr2): Cannot multiply different types";
errorMessage += " arr1.dataType()=";
errorMessage += DataTypeUtils::asString(arr1.dataType());
errorMessage += " arr2.dataType()=";
errorMessage += DataTypeUtils::asString(arr2.dataType());
errorMessage += " arr1.shapeInfo()=";
errorMessage += ShapeUtils::shapeAsString(arr1.shapeInfo());
errorMessage += " arr2.shapeInfo()=";
errorMessage += ShapeUtils::shapeAsString(arr2.shapeInfo());
errorMessage += " arr1.ordering()=";
THROW_EXCEPTION(errorMessage.c_str());
}
PointersManager pointersManager(arr1.getContext(), "operator*(T&& arr1, T&& arr2)");
if (arr1.lengthOf() == arr2.lengthOf() && arr1.rankOf() == arr2.rankOf()) {
const bool isArr1Rvalue = !std::is_reference<T1>::value && !arr1.isView();
const bool isArr2Rvalue = !std::is_reference<T2>::value && !arr2.isView();
NDArray *result = nullptr;
if (isArr1Rvalue) {
result = const_cast<NDArray *>(&arr1);
} else if (isArr2Rvalue) {
result = const_cast<NDArray *>(&arr2);
} else {
result = new NDArray(arr1.shapeInfo(), DataTypeUtils::pickPairwiseResultType(arr1.shapeInfo(), arr2.shapeInfo()),
false, arr1.getContext());
}
NDArray::prepareSpecialUse({result}, {&arr1, &arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1.getContext(), sd::pairwise::Multiply, arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(),
arr1.specialShapeInfo(), arr2.buffer(), arr2.shapeInfo(), arr2.specialBuffer(), arr2.specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {&arr1, &arr2});
return result;
}
NDArray* arr1Addr = &arr1;
NDArray* arr2Addr = &arr2;
return std::forward<T1>(arr1).applyTrueBroadcast(sd::BroadcastOpsTuple::Multiply(), arr2Addr);
}
template SD_LIB_EXPORT NDArray* operator*<NDArray &, NDArray &, void>(NDArray &arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator*<NDArray &, NDArray, void>(NDArray &arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* operator*<NDArray, NDArray &, void>(NDArray &&arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator*<NDArray&&, NDArray &&, void>(NDArray &&arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* sd::operator*<NDArray, NDArray, void>(sd::NDArray&&, sd::NDArray&&);
template <typename T1, typename T2, typename>
NDArray* operator+(T1 &&arr1, T2 &&arr2) {
if (arr1.isS() || arr2.isS())
THROW_EXCEPTION("operator+(T&& arr1, T&& arr2): you can't use this method on String arrays!");
if (arr1.dataType() != arr2.dataType() &&
(arr1.dataType() != DataType::BOOL || arr2.dataType() != BOOL)) {
std::string errorMessage;
errorMessage += "operator+(T&& arr1, T&& arr2): Cannot multiply different types";
errorMessage += " arr1.dataType()=";
errorMessage += DataTypeUtils::asString(arr1.dataType());
errorMessage += " arr2.dataType()=";
errorMessage += DataTypeUtils::asString(arr2.dataType());
errorMessage += " arr1.shapeInfo()=";
errorMessage += ShapeUtils::shapeAsString(arr1.shapeInfo());
errorMessage += " arr2.shapeInfo()=";
errorMessage += ShapeUtils::shapeAsString(arr2.shapeInfo());
errorMessage += " arr1.ordering()=";
THROW_EXCEPTION(errorMessage.c_str());
}
PointersManager pointersManager(arr1.getContext(), "operator+(T&& arr1, T&& arr2)");
if (arr1.lengthOf() == arr2.lengthOf() && arr1.rankOf() == arr2.rankOf()) {
const bool isArr1Rvalue = !std::is_reference<T1>::value && !arr1.isView();
const bool isArr2Rvalue = !std::is_reference<T2>::value && !arr2.isView();
NDArray *result = nullptr;
if (isArr1Rvalue)
result = const_cast<NDArray *>(&arr1);
else if (isArr2Rvalue)
result = const_cast<NDArray *>(&arr2);
else
result = new NDArray(arr1.shapeInfo(), DataTypeUtils::pickPairwiseResultType(arr1.shapeInfo(), arr2.shapeInfo()),
false, arr1.getContext());
NDArray::prepareSpecialUse({result}, {&arr1, &arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1.getContext(), sd::pairwise::Add, arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(),
arr1.specialShapeInfo(), arr2.buffer(), arr2.shapeInfo(), arr2.specialBuffer(), arr2.specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {&arr1, &arr2});
return result;
}
NDArray* arr1Addr = &arr1;
NDArray* arr2Addr = &arr2;
return std::forward<T1>(arr1).applyTrueBroadcast(sd::BroadcastOpsTuple::Add(), arr2Addr);
}
template SD_LIB_EXPORT NDArray* operator+<NDArray &, NDArray &, void>(NDArray &arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator+<NDArray &, NDArray, void>(NDArray &arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* operator+<NDArray, NDArray &, void>(NDArray &&arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator+<NDArray, NDArray, void>(NDArray &&arr1, NDArray &&arr2);
template <typename T1, typename T2, typename>
NDArray* operator/(T1 &&arr1, T2 &&arr2) {
if (arr1.isS() || arr2.isS())
THROW_EXCEPTION("operator/(T&& arr1, T&& arr2): you can't use this method on String arrays!");
if (!Environment::getInstance().isExperimentalBuild() && arr1.dataType() != arr2.dataType() &&
(arr1.dataType() != DataType::BOOL || arr2.dataType() != BOOL)) {
// Always throw exception - never use _exit(1) which kills the entire process
THROW_EXCEPTION(sd::datatype_exception::build("operator/(T&& arr1, T&& arr2): Cannot divide different types",
arr1.dataType(), arr2.dataType()).what());
}
PointersManager pointersManager(arr1.getContext(), "operator/(T&& arr1, T&& arr2)");
if (arr1.lengthOf() == arr2.lengthOf() && arr1.rankOf() == arr2.rankOf()) {
const bool isArr1Rvalue = !std::is_reference<T1>::value && !arr1.isView();
const bool isArr2Rvalue = !std::is_reference<T2>::value && !arr2.isView();
NDArray *result = nullptr;
if (isArr1Rvalue)
result = const_cast<NDArray *>(&arr1);
else if (isArr2Rvalue)
result = const_cast<NDArray *>(&arr2);
else
result = new NDArray(arr1.shapeInfo(), DataTypeUtils::pickPairwiseResultType(arr1.shapeInfo(), arr2.shapeInfo()),
false, arr1.getContext());
NDArray::prepareSpecialUse({result}, {&arr1, &arr2});
NativeOpExecutioner::execPairwiseTransform(
arr1.getContext(), sd::pairwise::Divide, arr1.buffer(), arr1.shapeInfo(), arr1.specialBuffer(),
arr1.specialShapeInfo(), arr2.buffer(), arr2.shapeInfo(), arr2.specialBuffer(), arr2.specialShapeInfo(),
result->buffer(), result->shapeInfo(), result->specialBuffer(), result->specialShapeInfo(), nullptr);
NDArray::registerSpecialUse({result}, {&arr1, &arr2});
return result;
}
NDArray* arr1Addr = &arr1;
NDArray* arr2Addr = &arr2;
return std::forward<T1>(arr1).applyTrueBroadcast(sd::BroadcastOpsTuple::Divide(), arr2Addr);
}
template SD_LIB_EXPORT NDArray* operator/<NDArray &, NDArray &, void>(NDArray &arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator/<NDArray &, NDArray, void>(NDArray &arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* operator/<NDArray, NDArray &, void>(NDArray &&arr1, NDArray &arr2);
template SD_LIB_EXPORT NDArray* operator/<NDArray&&, NDArray &&, void>(NDArray &&arr1, NDArray &&arr2);
template SD_LIB_EXPORT NDArray* operator/<NDArray, NDArray, void>(sd::NDArray&&, sd::NDArray&&);
} // namespace sd
+507
View File
@@ -0,0 +1,507 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
#ifndef CUDA_LAMBDA_HELPER
#define CUDA_LAMBDA_HELPER
#include <cuda.h>
#include <cuda_runtime.h>
#include <helpers/shape.h>
#include <system/op_boilerplate.h>
static sd::LongType SD_DEVICE length(const sd::LongType *shapeInfo) { return shape::length(shapeInfo); }
template <typename T, typename Lambda>
static SD_KERNEL void lambdaKernel(const void *vx, const sd::LongType *xShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda);
template <typename T, typename Lambda>
static SD_KERNEL void lambdaIndexedKernel(const void *vx, const sd::LongType *xShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda);
template <typename T, typename Lambda>
static SD_KERNEL void lambdaIndexedPairwiseKernel(const void *vx, const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda);
template <typename T, typename Lambda>
static SD_KERNEL void lambdaPairwiseKernel(const void *vx, const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz, const sd::LongType *zShapeInfo,
Lambda lambda);
template <typename T, typename Lambda>
static SD_KERNEL void lambdaPairwiseKernel(const void *scalarPtr, const void *vx, const sd::LongType *xShapeInfo, void *vz, const sd::LongType *zShapeInfo,
Lambda lambda);
template <typename T, typename Lambda>
static SD_KERNEL void lambdaTriplewiseKernel(const void *vw, const sd::LongType *wShapeInfo, const void *vx,
const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz, const sd::LongType *zShapeInfo,
Lambda lambda);
template <typename T>
class LambdaHelper {
public:
template <typename Lambda>
SD_INLINE static void lambdaLauncher(cudaStream_t *stream, const void *vx, const sd::LongType *xShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
lambdaKernel<T, Lambda><<<256, 512, 1024, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, lambda);
auto err = cudaStreamSynchronize(*stream);
if (err != 0) THROW_EXCEPTION("NDArray::applyLambda execution failed");
}
template <typename Lambda>
SD_INLINE static void lambdaIndexedLauncher(cudaStream_t *stream, const void *vx, const sd::LongType *xShapeInfo,
void *vz, const sd::LongType *zShapeInfo, Lambda lambda) {
lambdaIndexedKernel<T, Lambda><<<256, 512, 1024, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, lambda);
auto err = cudaStreamSynchronize(*stream);
if (err != 0) THROW_EXCEPTION("NDArray::applyIndexedLambda execution failed");
}
template <typename Lambda>
SD_INLINE static void lambdaPairwiseLauncher(cudaStream_t *stream, const void *vx, const sd::LongType *xShapeInfo, bool otherIsScalar,
const void *vy, const sd::LongType *yShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
if (otherIsScalar) {
lambdaPairwiseKernel<T, Lambda><<<256, 512, 1024, *stream>>>(vy, vx, xShapeInfo, vz, zShapeInfo, lambda);
} else {
lambdaPairwiseKernel<T, Lambda>
<<<256, 512, 1024, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, lambda);
}
auto err = cudaStreamSynchronize(*stream);
if (err != 0) THROW_EXCEPTION("NDArray::applyPairwiseLambda execution failed");
}
template <typename Lambda>
SD_INLINE static void lambdaIndexedPairwiseLauncher(cudaStream_t *stream, const void *vx,
const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
lambdaIndexedPairwiseKernel<T, Lambda>
<<<256, 512, 1024, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, lambda);
auto err = cudaStreamSynchronize(*stream);
if (err != 0) THROW_EXCEPTION("NDArray::applyIndexedPairwiseLambda execution failed");
}
template <typename Lambda>
SD_INLINE static void lambdaTriplewiseLauncher(cudaStream_t *stream, const void *vw, const sd::LongType *wShapeInfo,
const void *vx, const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
lambdaTriplewiseKernel<T, Lambda>
<<<256, 512, 1024, *stream>>>(vw, wShapeInfo, vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, lambda);
auto err = cudaStreamSynchronize(*stream);
if (err != 0) THROW_EXCEPTION("NDArray::applyTriplewiseLambda execution failed");
}
};
////////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaKernel(const void *vx, const sd::LongType *xShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
auto x = reinterpret_cast<const T *>(vx);
auto z = reinterpret_cast<T *>(vz);
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
}
__syncthreads();
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType zOffset;
INDEX2COORDS(e, xRank, xShape, xCoords);
COORDS2INDEX(xRank,xStride, xCoords, xOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
z[zOffset] = lambda(x[xOffset]);
}
}
////////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaIndexedKernel(const void *vx, const sd::LongType *xShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
auto x = reinterpret_cast<const T *>(vx);
auto z = reinterpret_cast<T *>(vz);
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
}
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType zOffset;
INDEX2COORDS(e, xRank, xShape, xCoords);
COORDS2INDEX(xRank,xStride, xCoords, xOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
z[zOffset] = lambda(e, x[xOffset]);
}
}
////////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaIndexedPairwiseKernel(const void *vx, const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz,
const sd::LongType *zShapeInfo, Lambda lambda) {
auto x = reinterpret_cast<const T *>(vx);
auto y = reinterpret_cast<const T *>(vy);
auto z = reinterpret_cast<T *>(vz);
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType yRank;
__shared__ sd::LongType *yShape;
__shared__ sd::LongType *yStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
yRank = shape::rank(yShapeInfo);
yShape = shape::shapeOf(yShapeInfo);
yStride = shape::stride(yShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
}
__syncthreads();
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType zOffset;
INDEX2COORDS(e, xRank,xShape, xCoords);
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
INDEX2COORDS(e, yRank, yShape, yCoords);
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank,zStride, zCoords, zOffset);
z[zOffset] = lambda(e, x[xOffset], y[yOffset]);
}
}
////////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaPairwiseKernel(const void *vx, const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz, const sd::LongType *zShapeInfo,
Lambda lambda) {
auto x = reinterpret_cast<const T *>(vx);
auto y = reinterpret_cast<const T *>(vy);
auto z = reinterpret_cast<T *>(vz);
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType yRank;
__shared__ sd::LongType *yShape;
__shared__ sd::LongType *yStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
yRank = shape::rank(yShapeInfo);
yShape = shape::shapeOf(yShapeInfo);
yStride = shape::stride(yShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
}
__syncthreads();
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType zOffset;
INDEX2COORDS(e, xRank, xShape, xCoords);
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
INDEX2COORDS(e, yRank, yShape, yCoords);
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank,zStride, zCoords, zOffset);
z[zOffset] = lambda(x[xOffset], y[yOffset]);
}
}
///////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaPairwiseKernel(const void *scalarPtr, const void *vx, const sd::LongType *xShapeInfo,
void *vz, const sd::LongType *zShapeInfo, Lambda lambda) {
auto x = reinterpret_cast<const T *>(vx);
auto y = reinterpret_cast<const T *>(scalarPtr);
auto z = reinterpret_cast<T *>(vz);
auto yVal = *y;
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType yRank;
__shared__ sd::LongType *yShape;
__shared__ sd::LongType *yStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
yRank = shape::rank(yShapeInfo);
yShape = shape::shapeOf(yShapeInfo);
yStride = shape::stride(yShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
}
__syncthreads();
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType zOffset;
INDEX2COORDS(e,xRank,xShape, xCoords);
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
z[zOffset] = lambda(x[xOffset], yVal);
}
}
////////////////////////////////////////////////////////////////////////
template <typename T, typename Lambda>
static SD_KERNEL void lambdaTriplewiseKernel(const void *vw, const sd::LongType *wShapeInfo, const void *vx,
const sd::LongType *xShapeInfo, const void *vy,
const sd::LongType *yShapeInfo, void *vz, const sd::LongType *zShapeInfo,
Lambda lambda) {
auto w = reinterpret_cast<const T *>(vw);
auto x = reinterpret_cast<const T *>(vx);
auto y = reinterpret_cast<const T *>(vy);
auto z = reinterpret_cast<T *>(vz);
auto zLength = length(zShapeInfo);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ sd::LongType xRank;
__shared__ sd::LongType *xShape;
__shared__ sd::LongType *xStride;
__shared__ sd::LongType yRank;
__shared__ sd::LongType *yShape;
__shared__ sd::LongType *yStride;
__shared__ sd::LongType zRank;
__shared__ sd::LongType *zShape;
__shared__ sd::LongType *zStride;
__shared__ sd::LongType wRank;
__shared__ sd::LongType *wShape;
__shared__ sd::LongType *wStride;
if(threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
yRank = shape::rank(yShapeInfo);
yShape = shape::shapeOf(yShapeInfo);
yStride = shape::stride(yShapeInfo);
zRank = shape::rank(zShapeInfo);
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
wRank = shape::rank(wShapeInfo);
wShape = shape::shapeOf(wShapeInfo);
wStride = shape::stride(wShapeInfo);
}
__syncthreads();
for (sd::LongType e = tid; e < zLength; e += blockDim.x * gridDim.x) {
sd::LongType wCoords[SD_MAX_RANK];
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType wOffset;
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType zOffset;
INDEX2COORDS(e, wRank, wShape, wCoords);
COORDS2INDEX(wRank, wStride, wCoords, wOffset);
INDEX2COORDS(e, xRank,xShape, xCoords);
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
INDEX2COORDS(e, yRank, yStride, yCoords);
COORDS2INDEX(yRank,yStride, yCoords, yOffset);
INDEX2COORDS(e, zRank, zShape, zCoords);
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
z[zOffset] = lambda(w[wOffset], x[xOffset], y[yOffset]);
}
}
#endif
//////////////////////////////////////////////////////////////////////////
template <typename Lambda>
void NDArray::applyLambda(Lambda func, NDArray *target) {
auto dtype = this->dataType();
if (dtype != target->dataType()) THROW_EXCEPTION("NDArray::applyLambda X/Z data types must be the same");
prepareSpecialUse({&target}, {this});
BUILD_SINGLE_SELECTOR(
dtype, LambdaHelper,
::lambdaLauncher(this->_context->getCudaStream(), this->specialBuffer(), this->specialShapeInfo(),
target->specialBuffer(), target->specialShapeInfo(), func),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this});
}
//////////////////////////////////////////////////////////////////////////
template <typename Lambda>
void NDArray::applyPairwiseLambda(NDArray *other, Lambda func, NDArray *target) {
auto dtype = this->dataType();
if (dtype != target->dataType() || dtype != other->dataType())
THROW_EXCEPTION("NDArray::applyPairwiseLambda X/Y/Z data types must be the same");
bool otherIsScalar = other->isScalar();
prepareSpecialUse({&target}, {this, &other});
BUILD_SINGLE_SELECTOR(
dtype, LambdaHelper,
::lambdaPairwiseLauncher(this->_context->getCudaStream(), this->specialBuffer(), this->specialShapeInfo(), otherIsScalar,
other->specialBuffer(), other->specialShapeInfo(), target->specialBuffer(),
target->specialShapeInfo(), func),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this, &other});
}
//////////////////////////////////////////////////////////////////////////
template <typename Lambda>
void NDArray::applyIndexedLambda(Lambda func, NDArray *target) {
auto dtype = this->dataType();
if (dtype != target->dataType())
THROW_EXCEPTION("NDArray::applyIndexedLambda X/Z data types must be the same");
prepareSpecialUse({&target}, {this});
BUILD_SINGLE_SELECTOR(
dtype, LambdaHelper,
::lambdaIndexedLauncher(this->_context->getCudaStream(), this->specialBuffer(), this->specialShapeInfo(),
target->specialBuffer(), target->specialShapeInfo(), func),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this});
}
//////////////////////////////////////////////////////////////////////////
template <typename Lambda>
void NDArray::applyIndexedPairwiseLambda(NDArray *other, Lambda func, NDArray *target) {
auto dtype = this->dataType();
if (dtype != target->dataType() || dtype != other->dataType())
THROW_EXCEPTION("NDArray::applyIndexedPairwiseLambda X/Y/Z data types must be the same");
prepareSpecialUse({&target}, {this, &other});
BUILD_SINGLE_SELECTOR(
dtype, LambdaHelper,
::lambdaIndexedPairwiseLauncher(this->_context->getCudaStream(), this->specialBuffer(), this->specialShapeInfo(),
other->specialBuffer(), other->specialShapeInfo(), target->specialBuffer(),
target->specialShapeInfo(), func),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this, &other});
}
//////////////////////////////////////////////////////////////////////////
template <typename Lambda>
void NDArray::applyTriplewiseLambda(NDArray *second, NDArray *third, Lambda func, NDArray *target) {
auto dtype = this->dataType();
if (dtype != target->dataType() || dtype != second.dataType() || dtype != third.dataType())
THROW_EXCEPTION("NDArray::applyTriplewiseLambda X/Y/Z data types must be the same");
prepareSpecialUse({&target}, {this, &second, &third});
BUILD_SINGLE_SELECTOR(
dtype, LambdaHelper,
::lambdaTriplewiseLauncher(this->_context->getCudaStream(), this->specialBuffer(), this->specialShapeInfo(),
second.specialBuffer(), second.specialShapeInfo(), third.specialBuffer(),
third.specialShapeInfo(), target->specialBuffer(), target->specialShapeInfo(), func),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this, &second, &third});
}
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// This class describes collection of NDArrays
//
// @author raver119!gmail.com
//
#ifndef NDARRAY_LIST_H
#define NDARRAY_LIST_H
#include <array/NDArray.h>
#include <memory/Workspace.h>
#include <system/common.h>
#include <atomic>
#include <string>
#include <unordered_map>
namespace sd {
class SD_LIB_EXPORT NDArrayList {
private:
// workspace where chunks belong to
// sd::memory::Workspace* _workspace = nullptr;
sd::LaunchContext *_context = sd::LaunchContext ::defaultContext();
// numeric and symbolic ids of this list
std::pair<int, int> _id;
std::string _name;
sd::DataType _dtype;
// stored chunks
SD_MAP_IMPL<int, sd::NDArray *> _chunks;
// just a counter, for stored elements
std::atomic<int> _elements;
std::atomic<int> _counter;
// reference shape
std::vector<sd::LongType> _shape;
// unstack axis
sd::LongType _axis = 0;
//
bool _expandable = false;
// maximum number of elements
int _height = 0;
public:
NDArrayList(int height, bool expandable = false);
~NDArrayList();
sd::DataType dataType();
NDArray *remove(int idx);
NDArray *read(int idx);
NDArray *readRaw(int idx);
sd::Status write(int idx, NDArray *array);
NDArray *pick(std::initializer_list<LongType> indices);
NDArray *pick(std::vector<LongType> &indices);
bool isWritten(int index);
std::vector<sd::LongType> &shape();
NDArray *stack();
void unstack(NDArray *array, LongType axis);
std::pair<int, int> &id();
std::string &name();
// sd::memory::Workspace* workspace();
sd::LaunchContext *context();
NDArrayList *clone();
bool equals(NDArrayList &other);
int elements();
int height();
int counter();
};
} // namespace sd
#endif
@@ -0,0 +1,42 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_POINTERDEALLOCATOR_H_
#define SD_POINTERDEALLOCATOR_H_
#include <system/common.h>
namespace sd {
class SD_LIB_EXPORT PointerDeallocator {
public:
PointerDeallocator() = default;
virtual ~PointerDeallocator() = default;
virtual void release(void *ptr);
};
} // namespace sd
#endif // SD_POINTERDEALLOCATOR_H_
+52
View File
@@ -0,0 +1,52 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_ARRAY_POINTER_H_
#define SD_ARRAY_POINTER_H_
#include <array/PointerDeallocator.h>
#include <memory>
namespace sd {
class SD_LIB_EXPORT PointerWrapper {
private:
void *_pointer = nullptr;
std::shared_ptr<PointerDeallocator> _deallocator;
public:
PointerWrapper(void *ptr, const std::shared_ptr<PointerDeallocator> &deallocator = {});
PointerWrapper() = default;
~PointerWrapper();
void *pointer() const;
template <typename T>
T *pointerAsT() const {
return reinterpret_cast<T *>(pointer());
}
};
} // namespace sd
#endif // SD_ARRAY_POINTER_H_
@@ -0,0 +1,40 @@
/*
* ******************************************************************************
* *
* *
* * 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
//
#ifndef SD_PRIMARYPOINTERDEALLOCATOR_H_
#define SD_PRIMARYPOINTERDEALLOCATOR_H_
#include <array/PointerDeallocator.h>
namespace sd {
class SD_LIB_EXPORT PrimaryPointerDeallocator : public PointerDeallocator {
public:
PrimaryPointerDeallocator() = default;
virtual ~PrimaryPointerDeallocator() = default;
void release(void *ptr) override;
};
} // namespace sd
#endif // SD_PRIMARYPOINTERDEALLOCATOR_H_
+78
View File
@@ -0,0 +1,78 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// This class is suited for execution results representation.
//
// PLESE NOTE: It will delete all stored NDArrays upon destructor call
//
// @author raver119@gmail.com
//
#ifndef LIBND4J_RESULTSET_H
#define LIBND4J_RESULTSET_H
#include <graph/generated/result_generated.h>
#include <system/common.h>
#include <vector>
namespace sd {
class NDArray; // forward declaration of template class NDArray
class SD_LIB_EXPORT ResultSet {
private:
std::vector<NDArray *> _content;
Status _status = Status::OK;
bool _removable = true;
void delContent();
public:
explicit ResultSet();
#ifndef __JAVACPP_HACK__
ResultSet(const ::graph::FlatResult *result);
#endif
ResultSet(const ResultSet &other) noexcept;
ResultSet &operator=(const ResultSet &other) noexcept;
// move constructor
ResultSet(ResultSet &&other) noexcept;
// move assignment operator
ResultSet &operator=(ResultSet &&other) noexcept;
~ResultSet();
int size();
NDArray *at(const unsigned long idx) const;
NDArray *operator[](const unsigned long idx) const;
void push_back(NDArray *array);
Status status();
void setStatus(Status status);
void purge();
void setNonRemovable();
void printIndexedBuffers();
};
} // namespace sd
#endif // LIBND4J_RESULTSET_H
@@ -0,0 +1,172 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// ShapeCacheLifecycleTracker - Tracks shape cache allocations and deallocations
// for memory leak detection.
// Only active when SD_GCC_FUNCTRACE is defined.
//
#ifndef LIBND4J_SHAPECACHELIFECYCLETRACKER_H
#define LIBND4J_SHAPECACHELIFECYCLETRACKER_H
#include <cstddef>
#include <string>
#include <atomic>
#include <mutex>
#include <ostream>
#include <system/common.h>
namespace sd {
namespace analysis {
class ComprehensiveLeakAnalyzer;
}
namespace array {
/**
* Statistics structure for shape cache lifecycle tracking
*/
struct ShapeCacheStats {
size_t totalAllocations = 0;
size_t totalDeallocations = 0;
size_t currentLive = 0;
size_t peakLive = 0;
size_t totalBytesAllocated = 0;
size_t totalBytesDeallocated = 0;
};
/**
* ShapeCacheLifecycleTracker - Singleton class for tracking shape cache allocations
* and deallocations for memory leak detection.
*
* This is a stub implementation that provides the expected interface.
* When SD_GCC_FUNCTRACE is enabled, this tracks shape cache lifecycle events.
*/
class ShapeCacheLifecycleTracker {
friend class sd::analysis::ComprehensiveLeakAnalyzer;
public:
/**
* Get singleton instance
*/
static ShapeCacheLifecycleTracker& getInstance() {
static ShapeCacheLifecycleTracker instance;
return instance;
}
/**
* Enable or disable tracking
*/
void setEnabled(bool enabled) {
_enabled.store(enabled);
}
/**
* Check if tracking is enabled
*/
bool isEnabled() const {
return _enabled.load();
}
/**
* Record a shape cache allocation
* @param shapeInfo Pointer to the shape info being allocated
*/
void recordAllocation(LongType* shapeInfo) {
if (!_enabled.load()) return;
std::lock_guard<std::mutex> lock(_mutex);
_stats.totalAllocations++;
_stats.currentLive++;
if (_stats.currentLive > _stats.peakLive) {
_stats.peakLive = _stats.currentLive;
}
}
/**
* Record a shape cache deallocation
* @param shapeInfo Pointer to the shape info being deallocated
*/
void recordDeallocation(LongType* shapeInfo) {
if (!_enabled.load()) return;
std::lock_guard<std::mutex> lock(_mutex);
_stats.totalDeallocations++;
if (_stats.currentLive > 0) {
_stats.currentLive--;
}
}
/**
* Get statistics
*/
ShapeCacheStats getStats() const {
return _stats;
}
/**
* Print statistics to output stream
*/
void printStatistics(std::ostream& out) const {
out << "ShapeCache Statistics: allocations=" << _stats.totalAllocations
<< ", deallocations=" << _stats.totalDeallocations
<< ", live=" << _stats.currentLive << "\n";
}
/**
* Print current memory leaks to output stream
*/
void printCurrentLeaks(std::ostream& out) const {
out << "ShapeCache Current Leaks: " << _stats.currentLive << " shapes\n";
}
/**
* Log shape info for a specific address (for crash debugging)
* @param address Address to look up
* @param out Output stream
* @return true if shape was found
*/
bool logShapeForAddress(void* address, std::ostream& out) const {
std::lock_guard<std::mutex> lock(_mutex);
out << "ShapeCache address lookup for " << address << ": ";
out << "tracking " << (_enabled.load() ? "enabled" : "disabled");
out << ", live shapes: " << _stats.currentLive << "\n";
return false;
}
private:
ShapeCacheLifecycleTracker() : _enabled(false) {}
~ShapeCacheLifecycleTracker() = default;
// Disable copy and move
ShapeCacheLifecycleTracker(const ShapeCacheLifecycleTracker&) = delete;
ShapeCacheLifecycleTracker& operator=(const ShapeCacheLifecycleTracker&) = delete;
ShapeCacheLifecycleTracker(ShapeCacheLifecycleTracker&&) = delete;
ShapeCacheLifecycleTracker& operator=(ShapeCacheLifecycleTracker&&) = delete;
std::atomic<bool> _enabled;
mutable std::mutex _mutex;
ShapeCacheStats _stats;
};
} // namespace array
} // namespace sd
#endif // LIBND4J_SHAPECACHELIFECYCLETRACKER_H
+257
View File
@@ -0,0 +1,257 @@
/* ******************************************************************************
*
*
* 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
#ifndef DEV_TESTS_SHAPEDESCRIPTOR_H
#define DEV_TESTS_SHAPEDESCRIPTOR_H
#include <array/ArrayOptions.hXX>
#include <array/DataType.h>
#include <helpers/shape.h>
#include <system/common.h>
#include <initializer_list>
#include <unordered_map>
#include <vector>
namespace sd {
#define SHAPE_DESC_OK 0
#define SHAPE_DESC_INCORRECT_STRIDES 1 // strides does not match shapes
#define SHAPE_DESC_INCORRECT_EWS 2 // ews neither matches stride nor continuity
#define SHAPE_DESC_INCORRECT_RANK 4 // rank > 32 or shape size and rank does not match
#define SHAPE_DESC_INVALID_EMPTY 5 // rank > 32 or shape size and rank does not match
class SD_LIB_EXPORT ShapeDescriptor {
private:
int _rank = 0;
LongType * _shape_strides = nullptr;
char _order = 'c';
DataType _dataType;
LongType _extraProperties = 0;
LongType _paddedAllocSize = 0;
LongType _offset = 0;
public:
bool ownsShapeStrides = false;
// Hash caching
mutable uint64_t _cached_hash;
mutable bool _hash_computed;
#ifndef __JAVACPP_HACK__
#if defined(SD_GCC_FUNCTRACE)
StackTrace st;
//stack trace when stored in cache.
StackTrace storeStackTrace;
#endif
ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape, LongType extras);
ShapeDescriptor(const ShapeDescriptor &other);
ShapeDescriptor(const LongType *shapeInfo, bool validateDataType = true, bool overrideStrides = false);
explicit ShapeDescriptor(const LongType *shapeInfo, const DataType dtypeOverride, const bool overrideStrides);
explicit ShapeDescriptor(const LongType *shapeInfo, const LongType *dtypeOverride);
explicit ShapeDescriptor(const LongType *shapeInfo, const LongType *dtypeOverride,
const LongType *orderOverride);
explicit ShapeDescriptor(const DataType type, const LongType length);
explicit ShapeDescriptor(const DataType type, const char order, const LongType *shape, const LongType rank);
explicit ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape);
explicit ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape,
const std::vector<LongType> &strides);
explicit ShapeDescriptor(const DataType type, const char order, const std::vector<LongType> &shape,
const std::vector<LongType> &strides, const LongType ews);
explicit ShapeDescriptor(const DataType type, const char order, const LongType *shape,
const LongType *strides, const LongType rank, LongType extras);
ShapeDescriptor() = default;
~ShapeDescriptor();
#endif
int rank() const;
void invalidateHash() const {
_hash_computed = false;
_cached_hash = 0;
}
uint64_t getCachedHash() const {
return _cached_hash;
}
LongType arrLength() const;
LongType offset();
char order() const;
DataType dataType() const;
bool isEmpty() const;
sd::LongType * shape_strides();
const LongType *stridesPtr() const;
LongType extra() const {
return _extraProperties;
}
void collectStoreStackTrace();
void print() const;
// returns minimal allocation length
LongType allocLength() const;
// returns Status for the correctness
LongType validate() const;
// we use default copy assignment operator
// Modify assignment operator to reset hash cache:
ShapeDescriptor& operator=(const ShapeDescriptor& other) {
if (this != &other) {
// Existing cleanup code
if (_shape_strides != nullptr && ownsShapeStrides) {
delete[] _shape_strides;
_shape_strides = nullptr;
}
// Copy all basic members
_rank = other._rank;
_extraProperties = other._extraProperties;
_dataType = other._dataType;
_order = other._order;
_paddedAllocSize = other._paddedAllocSize;
_offset = other._offset;
// Reset hash cache
_cached_hash = 0;
_hash_computed = false;
// Handle shape_strides - make a deep copy if source has data
if (other._shape_strides != nullptr) {
const int size = (_rank < 1 ? 1 : _rank) * 2;
_shape_strides = new LongType[size];
std::memcpy(_shape_strides, other._shape_strides, size * sizeof(LongType));
ownsShapeStrides = true;
} else {
_shape_strides = nullptr;
ownsShapeStrides = false;
}
}
return *this;
}
// we use default move assignment operator
ShapeDescriptor &operator=(ShapeDescriptor &&other) noexcept = default;
// equal to operator
bool operator==(const ShapeDescriptor &other) const;
// less than operator
bool operator<(const ShapeDescriptor &other) const;
LongType *toShapeInfo() const;
const char * toString() {
std::string message;
message += " Rank:" ;
message += std::to_string(_rank);
message += " Shape and Strides:";
if(_shape_strides == nullptr) {
message += " Null";
} else {
for (int i = 0; i < _rank * 2; i++) {
message += " ";
message += std::to_string(_shape_strides[i]);
}
}
message += "Data type:";
message += std::to_string(_dataType);
message += " Order:";
message += std::to_string(_order);
message += " Extra Properties:";
message += std::to_string(_extraProperties);
message += " Padded Alloc Size: ";
message += std::to_string(_paddedAllocSize);
message += " Offset: ";
message += std::to_string(_offset);
//need this in order to avoid deallocation
std::string *ret = new std::string(message.c_str());
return ret->c_str();
}
static ShapeDescriptor * emptyDescriptor(const DataType type);
static ShapeDescriptor * scalarDescriptor(const DataType type);
static ShapeDescriptor * vectorDescriptor(const LongType length, const DataType type);
// create Descriptor with padded buffer.
static ShapeDescriptor * paddedBufferDescriptor(const DataType type, const char order,
const std::vector<LongType> &shape,
const std::vector<LongType> &paddings);
static const char *messageForShapeDescriptorError(const int errorCode) {
switch (errorCode) {
case SHAPE_DESC_OK:
return "OK";
case SHAPE_DESC_INCORRECT_STRIDES:
return "Incorrect strides";
case SHAPE_DESC_INCORRECT_EWS:
return "Incorrect ews";
case SHAPE_DESC_INCORRECT_RANK:
return "Incorrect rank";
case SHAPE_DESC_INVALID_EMPTY:
return "Invalid empty";
default:
return "Unknown error";
}
}
bool isScalar() const;
SD_INLINE void fillStrides() {
if(_rank == 0) {
return;
}
if(_shape_strides == nullptr) {
return;
}
// double checks if the _rank and _shape_strides are set correctly before filling strides
auto _shape = _shape_strides;
auto _strides = _shape_strides + _rank;
if (_rank > 0) {
if (_order == 'c')
shape::calcStrides(_shape, _rank, _strides);
else
shape::calcStridesFortran(_shape, _rank, _strides);
} else {
for (int i = 0; i < _rank; i++) {
_strides[i] = 0;
}
}
}
};
} // namespace sd
#ifndef __JAVACPP_HACK__
namespace std {
template <>
class SD_LIB_EXPORT hash<sd::ShapeDescriptor> {
public:
size_t operator()(sd::ShapeDescriptor k) const;
};
} // namespace std
#endif
#endif // DEV_TESTS_SHAPEDESCRIPTOR_H
+59
View File
@@ -0,0 +1,59 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef LIBND4J_SHAPELIST_H
#define LIBND4J_SHAPELIST_H
#include <helpers/shape.h>
#include <system/common.h>
#include <vector>
namespace sd {
class SD_LIB_EXPORT ShapeList {
protected:
std::vector< sd::LongType *> _shapes;
bool _destroyed = false;
bool _autoremovable = false;
bool _workspace = false;
public:
ShapeList( sd::LongType *shape = nullptr);
ShapeList(const std::vector< sd::LongType *> &shapes, bool isWorkspace);
ShapeList(const std::vector< sd::LongType *> &shapes);
~ShapeList();
void destroy();
int size() const;
sd::LongType *at(int idx);
void push_back( sd::LongType *shape);
/**
* PLEASE NOTE: This method should be called ONLY if shapes were generated at workspaces. Otherwise you'll get memory
* leak
*/
void detach();
};
} // namespace sd
#endif // LIBND4J_SHAPELIST_H
+34
View File
@@ -0,0 +1,34 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef ND4J_SPACE_TYPE_H
#define ND4J_SPACE_TYPE_H
namespace sd {
enum SpaceType {
CONTINUOUS = 1,
COMPLEX = 2,
QUANTIZED = 3,
};
}
#endif
+35
View File
@@ -0,0 +1,35 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef LIBND4J_SPARSETYPE_H
#define LIBND4J_SPARSETYPE_H
namespace sd {
enum SparseType {
CSR = 1,
CSC = 2,
COO = 3,
LIL = 4,
};
}
#endif // LIBND4J_SPARSETYPE_H
@@ -0,0 +1,261 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
//
// TADCacheLifecycleTracker - Tracks TAD (Tensor Along Dimension) cache allocations
// and deallocations for memory leak detection.
// Only active when SD_GCC_FUNCTRACE is defined.
//
#ifndef LIBND4J_TADCACHELIFECYCLETRACKER_H
#define LIBND4J_TADCACHELIFECYCLETRACKER_H
#include <cstddef>
#include <string>
#include <vector>
#include <atomic>
#include <mutex>
#include <map>
#include <ostream>
#include <fstream>
#include <system/common.h>
namespace sd {
// Forward declaration
class TadPack;
namespace analysis {
class ComprehensiveLeakAnalyzer;
}
namespace array {
/**
* Statistics structure for TAD cache lifecycle tracking
*/
struct TADCacheStats {
size_t totalAllocations = 0;
size_t totalDeallocations = 0;
size_t currentLive = 0;
size_t peakLive = 0;
size_t totalBytesAllocated = 0;
size_t totalBytesDeallocated = 0;
};
/**
* TADCacheLifecycleTracker - Singleton class for tracking TAD cache allocations
* and deallocations for memory leak detection.
*
* This is a stub implementation that provides the expected interface.
* When SD_GCC_FUNCTRACE is enabled, this tracks TAD cache lifecycle events.
*/
class TADCacheLifecycleTracker {
friend class sd::analysis::ComprehensiveLeakAnalyzer;
public:
/**
* Get singleton instance
*/
static TADCacheLifecycleTracker& getInstance() {
static TADCacheLifecycleTracker instance;
return instance;
}
/**
* Enable or disable tracking
*/
void setEnabled(bool enabled) {
_enabled.store(enabled);
}
/**
* Check if tracking is enabled
*/
bool isEnabled() const {
return _enabled.load();
}
/**
* Record a TAD cache allocation
* @param tadPack Pointer to the TadPack being allocated
* @param numTads Number of TADs
* @param shapeInfoBytes Size of shape info in bytes
* @param offsetsBytes Size of offsets in bytes
* @param dimensions Dimensions vector
*/
void recordAllocation(void* tadPack, LongType numTads,
size_t shapeInfoBytes, size_t offsetsBytes,
const std::vector<LongType>& dimensions) {
if (!_enabled.load()) return;
std::lock_guard<std::mutex> lock(_mutex);
_stats.totalAllocations++;
_stats.currentLive++;
size_t totalBytes = shapeInfoBytes + offsetsBytes;
_stats.totalBytesAllocated += totalBytes;
if (_stats.currentLive > _stats.peakLive) {
_stats.peakLive = _stats.currentLive;
}
}
/**
* Record a TAD cache deallocation
* @param tadPack Pointer to the TadPack being deallocated
*/
void recordDeallocation(void* tadPack) {
if (!_enabled.load()) return;
std::lock_guard<std::mutex> lock(_mutex);
_stats.totalDeallocations++;
if (_stats.currentLive > 0) {
_stats.currentLive--;
}
}
/**
* Get statistics
*/
TADCacheStats getStats() const {
return _stats;
}
/**
* Print statistics to output stream
*/
void printStatistics(std::ostream& out) const {
out << "TADCache Statistics: allocations=" << _stats.totalAllocations
<< ", deallocations=" << _stats.totalDeallocations
<< ", live=" << _stats.currentLive << "\n";
}
/**
* Print current memory leaks to output stream
*/
void printCurrentLeaks(std::ostream& out) const {
out << "TADCache Current Leaks: " << _stats.currentLive << " TadPacks\n";
}
/**
* Log TAD info for a specific address (for crash debugging)
* @param address Address to look up
* @param out Output stream
* @return true if TAD was found
*/
bool logTADForAddress(void* address, std::ostream& out) const {
std::lock_guard<std::mutex> lock(_mutex);
out << "TADCache address lookup for " << address << ": ";
out << "tracking " << (_enabled.load() ? "enabled" : "disabled");
out << ", live TadPacks: " << _stats.currentLive << "\n";
return false;
}
/**
* Generate temporal leak report
* @param outputPath Path to write report
* @param windowCount Number of time windows
* @param windowDurationSec Duration of each window in seconds
*/
void generateTemporalLeakReport(const std::string& outputPath, int windowCount, double windowDurationSec) const {
std::lock_guard<std::mutex> lock(_mutex);
std::ofstream out(outputPath);
if (out.is_open()) {
out << "=== TADCache Temporal Leak Report ===\n";
out << "Note: Temporal tracking requires timestamp storage - not yet implemented\n";
out << "Current Statistics:\n";
out << " Total Allocations: " << _stats.totalAllocations << "\n";
out << " Total Deallocations: " << _stats.totalDeallocations << "\n";
out << " Current Live: " << _stats.currentLive << "\n";
out.close();
}
}
/**
* Capture a leak snapshot for later comparison
* @return Snapshot ID
*/
LongType captureLeakSnapshot() {
std::lock_guard<std::mutex> lock(_mutex);
LongType id = _nextSnapshotId++;
_snapshots[id] = _stats;
return id;
}
/**
* Generate diff between two snapshots
* @param snapshot1 First snapshot ID
* @param snapshot2 Second snapshot ID
* @param outputPath Path to write diff report
*/
void generateSnapshotDiff(LongType snapshot1, LongType snapshot2, const std::string& outputPath) const {
std::lock_guard<std::mutex> lock(_mutex);
std::ofstream out(outputPath);
if (out.is_open()) {
out << "=== TADCache Snapshot Diff Report ===\n";
out << "Comparing snapshot " << snapshot1 << " to snapshot " << snapshot2 << "\n\n";
auto it1 = _snapshots.find(snapshot1);
auto it2 = _snapshots.find(snapshot2);
if (it1 == _snapshots.end()) {
out << "ERROR: Snapshot " << snapshot1 << " not found\n";
} else if (it2 == _snapshots.end()) {
out << "ERROR: Snapshot " << snapshot2 << " not found\n";
} else {
const auto& s1 = it1->second;
const auto& s2 = it2->second;
out << "Allocations: " << s1.totalAllocations << " -> " << s2.totalAllocations;
out << " (diff: " << (long long)(s2.totalAllocations - s1.totalAllocations) << ")\n";
out << "Deallocations: " << s1.totalDeallocations << " -> " << s2.totalDeallocations;
out << " (diff: " << (long long)(s2.totalDeallocations - s1.totalDeallocations) << ")\n";
out << "Live: " << s1.currentLive << " -> " << s2.currentLive;
out << " (diff: " << (long long)(s2.currentLive - s1.currentLive) << ")\n";
}
out.close();
}
}
/**
* Clear all snapshots
*/
void clearSnapshots() {
std::lock_guard<std::mutex> lock(_mutex);
_snapshots.clear();
}
private:
TADCacheLifecycleTracker() : _enabled(false), _nextSnapshotId(1) {}
~TADCacheLifecycleTracker() = default;
// Disable copy and move
TADCacheLifecycleTracker(const TADCacheLifecycleTracker&) = delete;
TADCacheLifecycleTracker& operator=(const TADCacheLifecycleTracker&) = delete;
TADCacheLifecycleTracker(TADCacheLifecycleTracker&&) = delete;
TADCacheLifecycleTracker& operator=(TADCacheLifecycleTracker&&) = delete;
std::atomic<bool> _enabled;
mutable std::mutex _mutex;
TADCacheStats _stats;
std::atomic<LongType> _nextSnapshotId;
std::map<LongType, TADCacheStats> _snapshots;
};
} // namespace array
} // namespace sd
#endif // LIBND4J_TADCACHELIFECYCLETRACKER_H
+81
View File
@@ -0,0 +1,81 @@
/* ******************************************************************************
*
* Copyright (c) 2024 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.
*
* 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 Adam Gibson
//
#ifndef DEV_TESTS_TADCALCULATOR_H
#define DEV_TESTS_TADCALCULATOR_H
#include <array/TadPack.h>
#include <system/common.h>
#include <helpers/ConstantHelper.h>
#include <helpers/ConstantShapeHelper.h>
#include <array/ConstantShapeBuffer.h>
#include <array/ConstantOffsetsBuffer.h>
#include <vector>
#include <memory>
namespace sd {
/**
* TadCalculator handles the computation of Tensor Along Dimension (TAD) information
* including shapes and offsets for sub-arrays.
*/
class SD_LIB_EXPORT TadCalculator {
private:
LongType* _originalShape; // Original shape info pointer
ConstantShapeBuffer *_tadShape; // Calculated TAD shape buffer
ConstantOffsetsBuffer *_tadOffsets; // Calculated TAD offsets buffer
LongType _numTads; // Number of TADs
public:
/**
* Constructor for TadCalculator
* @param originalShape Pointer to the original shape information
*/
explicit TadCalculator(LongType* originalShape);
~TadCalculator();
/**
* Creates a TAD pack for the given dimensions
* @param dimensions Vector of dimensions to calculate TADs for
*/
void createTadPack(const std::vector<LongType>& dimensions);
/**
* Returns the calculated TAD shape buffer
* @return ConstantShapeBuffer containing TAD shape information
*/
ConstantShapeBuffer *tadShape() const { return _tadShape; }
/**
* Returns the calculated TAD offsets buffer
* @return ConstantOffsetsBuffer containing TAD offset information
*/
ConstantOffsetsBuffer *tadOffsets() const { return _tadOffsets; }
/**
* Returns the number of TADs calculated
* @return Number of TADs
*/
LongType numberOfTads() const { return _numTads; }
};
} // namespace sd
#endif // DEV_TESTS_TADCALCULATOR_H
+74
View File
@@ -0,0 +1,74 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef DEV_TESTS_TADDESCRIPTOR_H
#define DEV_TESTS_TADDESCRIPTOR_H
#include <array/ShapeDescriptor.h>
namespace sd {
class SD_LIB_EXPORT TadDescriptor {
private:
sd::LongType *_originalShape;
std::vector<LongType> _axis;
bool _unitiesInShape;
public:
explicit TadDescriptor(const LongType *originalShape, const LongType *dimensions, const LongType length,
const bool keepUnitiesInShape = false);
~TadDescriptor() = default;
// NCC has issues with copy constructors
TadDescriptor &operator=(const TadDescriptor &other) = default;
// we use default move assignment operator
TadDescriptor &operator=(TadDescriptor &&other) noexcept = default;
explicit TadDescriptor(const TadDescriptor &other);
// equal to operator
bool operator==(const TadDescriptor &other) const;
// less than operator
bool operator<(const TadDescriptor &other) const;
std::vector<LongType> &axis();
LongType *originalShape();
bool areUnitiesinShape() const;
};
} // namespace sd
#ifndef __JAVACPP_HACK__
namespace std {
template <>
class SD_LIB_EXPORT hash<sd::TadDescriptor> {
public:
size_t operator()(const sd::TadDescriptor &k) const;
};
} // namespace std
#endif
#endif // DEV_TESTS_TADDESCRIPTOR_H
+127
View File
@@ -0,0 +1,127 @@
/* ******************************************************************************
*
*
* 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
//
#ifndef DEV_TESTS_TADPACK_H
#define DEV_TESTS_TADPACK_H
#include <array/ConstantOffsetsBuffer.h>
#include <array/ConstantShapeBuffer.h>
#include <system/common.h>
#include <array/NDArray.h>
#ifndef __JAVACPP_HACK__
namespace sd {
class SD_LIB_EXPORT TadPack {
private:
ConstantShapeBuffer *_tadShape;
ConstantOffsetsBuffer *_tadOffsets;
LongType _numTads = 0;
LongType _shapeInfoLength = 0;
LongType* _dimensions = nullptr;
LongType _dimensionsLength = 0;
size_t _packHash = 0; // Cache the hash for quick comparison
public:
explicit TadPack( ConstantShapeBuffer *shapes,
ConstantOffsetsBuffer *offets, LongType numTads,
LongType* dimensions = nullptr, LongType dimLength = 0);
TadPack() = default;
~TadPack() {};
LongType* primaryShapeInfo();
LongType* primaryOffsets();
LongType* specialShapeInfo();
LongType* specialOffsets();
LongType numberOfTads() const;
LongType shapeInfoLength();
/**
* Extracts an NDArray view for the given TAD index.
* @param input The input NDArray.
* @param tadIndex The index of the TAD to extract.
* @return A new NDArray view representing the TAD.
*/
NDArray *extractTadView(NDArray* input, sd::LongType tadIndex) {
auto shapeInfo = primaryShapeInfo();
auto offsets = primaryOffsets();
auto tadOffset = offsets[tadIndex];
auto x = input->buffer();
NDArray *ret = new NDArray(x,shapeInfo,LaunchContext::defaultContext(),false,tadOffset);
return ret;
}
void computeHash() {
size_t hash = 17;
// Add dimensions to hash
for (LongType i = 0; i < _dimensionsLength; i++) {
hash = hash * 31 + static_cast<size_t>(_dimensions[i]);
}
// Add shape info to hash if available
LongType* primaryShape = primaryShapeInfo();
if (primaryShape) {
int rank = shape::rank(primaryShape);
// Add rank
hash = hash * 13 + static_cast<size_t>(rank);
// Add shape dimensions
LongType* shapeValues = shape::shapeOf(primaryShape);
for (int i = 0; i < rank; i++) {
hash = hash * 17 + static_cast<size_t>(shapeValues[i]);
}
// Add strides
LongType* strides = shape::stride(primaryShape);
for (int i = 0; i < rank; i++) {
hash = hash * 23 + static_cast<size_t>(strides[i]);
}
// Add order and data type
hash = hash * 29 + static_cast<size_t>(shape::order(primaryShape));
hash = hash * 37 + static_cast<size_t>(ArrayOptions::dataType(primaryShape));
}
// Add number of TADs
hash = hash * 41 + static_cast<size_t>(_numTads);
_packHash = hash;
}
/**
* These methods return either primary or special pointers depending on platform binaries were compiled for
* @return
*/
LongType* platformShapeInfo();
LongType* platformOffsets();
void print(const char* msg);
bool operator==( TadPack& other);
};
} // namespace sd
#endif // DEV_TESTS_TADPACK_H
#endif
+372
View File
@@ -0,0 +1,372 @@
/* ******************************************************************************
*
*
* 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 <types/types.h>
#include <system/type_boilerplate.h>
namespace sd {
void DataBuffer::expand(const uint64_t size) {
if (static_cast<LongType>(size) > _lenInBytes) {
// allocate new buffer
int8_t* newBuffer = nullptr;
ALLOCATE(newBuffer, _workspace, size, int8_t);
// copy data from existing buffer
std::memcpy(newBuffer, _primaryBuffer, _lenInBytes);
if (_isOwnerPrimary) {
RELEASE(reinterpret_cast<int8_t*>(_primaryBuffer), _workspace);
}
_primaryBuffer = newBuffer;
_lenInBytes = size;
_isOwnerPrimary = true;
}
}
template <typename T>
void DataBuffer::printHostBufferContent(void* buffer, sd::LongType offset, sd::LongType length) {
T* typedBuffer = reinterpret_cast<T*>(buffer);
sd_printf("[ ", 0);
for (sd::LongType i = offset; i < offset + length; i++) {
// For numeric types, cast to double for consistent formatting
if (std::is_arithmetic<T>::value) {
sd_printf("%g ", (double)typedBuffer[i]);
} else {
// For non-numeric types, print as hex
sd_printf("0x%x ", *reinterpret_cast<int*>(&typedBuffer[i]));
}
}
sd_printf("]", 0);
}
BUILD_SINGLE_TEMPLATE(void DataBuffer::printHostBufferContent,(void* buffer, sd::LongType offset, sd::LongType length),SD_COMMON_TYPES);
// DataBuffer implementation for .cpp file
void DataBuffer::printBufferDebug(const char* msg, sd::LongType offset, sd::LongType limit) {
if (msg) sd_printf("%s:\n", msg);
// Print metadata
sd_printf("DataBuffer: DataType=%s, Length=%lld elements, DeviceId=%d\n",
DataTypeUtils::asString(_dataType).c_str(), (long long)getNumElements(), deviceId());
// Print host buffer content
if (_primaryBuffer != nullptr) {
sd_printf("Host buffer (@%p): ", _primaryBuffer);
sd::LongType len = getNumElements();
sd::LongType printLen = limit < 0 ? len : std::min(len - offset, limit);
// Print based on datatype
BUILD_SINGLE_SELECTOR(_dataType, printHostBufferContent,
(_primaryBuffer, offset, printLen), SD_COMMON_TYPES);
if (offset + printLen < len) sd_printf("... ", 0);
sd_printf("\n", 0);
} else {
sd_printf("Host buffer: nullptr\n", 0);
}
// Print device info but not content (CPU version)
if (_specialBuffer != nullptr) {
sd_printf("Device buffer (@%p): [Not accessible from CPU]\n", _specialBuffer);
} else {
sd_printf("Device buffer: nullptr\n", 0);
}
#if defined(SD_CUDA)
// Print sync state counters
sd_printf("Sync state: _counter=%lld, _writePrimary=%lld, _writeSpecial=%lld, _readPrimary=%lld, _readSpecial=%lld\n",
(long long)_counter.load(), (long long)_writePrimary.load(), (long long)_writeSpecial.load(),
(long long)_readPrimary.load(), (long long)_readSpecial.load());
sd_printf("isPrimaryActual=%d, isSpecialActual=%d\n", isPrimaryActual(), isSpecialActual());
#endif
}
// Helper template to print host buffer content
template <typename T>
void printHostBufferContent(void* buffer, sd::LongType offset, sd::LongType length) {
T* typedBuffer = reinterpret_cast<T*>(buffer);
sd_printf("[ ", 0);
for (sd::LongType i = offset; i < offset + length; i++) {
// For numeric types, cast to double for consistent formatting
if (std::is_arithmetic<T>::value) {
sd_printf("%g ", (double)typedBuffer[i]);
} else {
// For non-numeric types, print as hex
sd_printf("0x%x ", *reinterpret_cast<int*>(&typedBuffer[i]));
}
}
sd_printf("]", 0);
}
void DataBuffer::printSpecialAllocationTraces() {
//no op on purpose
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::allocateBuffers(const bool allocBoth) { // always allocate primary buffer only (cpu case)
allocatePrimary();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyBufferFrom(const DataBuffer& other,
size_t sizeToCopyinBytes,
const sd::LongType offsetThis,
const sd::LongType offsetOther) {
if(other._dataType != _dataType) {
THROW_EXCEPTION("DataBuffer::copyBufferFrom: data types of buffers are different");
}
if (sizeToCopyinBytes == 0) {
LongType otherBytes = other.getLenInBytes() - offsetOther;
LongType thisBytes = getLenInBytes() - offsetThis;
sizeToCopyinBytes = otherBytes < thisBytes ? otherBytes : thisBytes;
}
if (sizeToCopyinBytes == 0) return;
if(static_cast<LongType>(sizeToCopyinBytes) > other._lenInBytes - offsetOther) {
std::string errorMessage;
errorMessage = "DataBuffer::copyBufferFrom: size to copy is larger than source buffer ";
errorMessage += std::to_string(sizeToCopyinBytes);
errorMessage += " > ";
errorMessage += std::to_string(other._lenInBytes - offsetOther);
THROW_EXCEPTION(errorMessage.c_str());
}
if(sizeToCopyinBytes > getLenInBytes() - offsetThis) {
std::string errorMessage;
errorMessage = "DataBuffer::copyBufferFrom: size to copy is larger than destination buffer ";
errorMessage += std::to_string(sizeToCopyinBytes);
errorMessage += " > ";
errorMessage += std::to_string(getLenInBytes() - offsetThis);
THROW_EXCEPTION(errorMessage.c_str());
}
if (other._primaryBuffer != nullptr) {
auto sizeOfElement = DataTypeUtils::sizeOfElement(_dataType);
auto sizeOfOtherElement = DataTypeUtils::sizeOfElement(_dataType);
if(sizeOfElement != sizeOfOtherElement) {
THROW_EXCEPTION("DataBuffer::copyBufferFrom: size of elements in buffers are different");
}
std::memcpy(
static_cast<int8_t*>(_primaryBuffer) + offsetThis * sizeOfElement,
static_cast<const int8_t*>(other._primaryBuffer) + offsetOther * sizeOfOtherElement,
sizeToCopyinBytes);
}
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyBufferFromHost(const void* hostBuffer, size_t sizeToCopyinBytes, const sd::LongType offsetThis,
const sd::LongType offsetHostBuffer) {
if (sizeToCopyinBytes == 0) sizeToCopyinBytes = getLenInBytes();
if (sizeToCopyinBytes == 0) return;
if (hostBuffer != nullptr)
std::memcpy(static_cast<int8_t*>(_primaryBuffer) + offsetThis * DataTypeUtils::sizeOfElement(_dataType),
static_cast<const int8_t*>(hostBuffer) + offsetHostBuffer * DataTypeUtils::sizeOfElement(_dataType),
sizeToCopyinBytes);
}
/////////////////////////
template <typename T>
void memcpyWithT(DataBuffer* dst, DataBuffer* src, sd::LongType startingOffset, sd::LongType dstOffset) {
if(src->getLenInBytes() != dst->getLenInBytes()) {
THROW_EXCEPTION("DataBuffer::memcpy: source and destination buffers have different length in bytes");
}
std::memcpy(dst->primaryAtOffset<T>(dstOffset), src->primaryAtOffset<T>(startingOffset), src->getLenInBytes());
dst->readPrimary();
}
void DataBuffer::memcpy(DataBuffer* dst, DataBuffer* src,
sd::LongType startingOffset, sd::LongType dstOffset) {
BUILD_SINGLE_SELECTOR(dst->_dataType, memcpyWithT,(dst, src, startingOffset, dstOffset),
SD_COMMON_TYPES);
dst->readPrimary();
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void DataBuffer::deleteSpecial() {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::syncToPrimary(const LaunchContext* context, const bool forceSync) {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setCountersToZero() {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyCounters(const DataBuffer& other) {}
void DataBuffer::writePrimary() const {}
void DataBuffer::writeSpecial() const {}
void DataBuffer::readPrimary() const {}
void DataBuffer::readSpecial() const {}
bool DataBuffer::isPrimaryActual() const { return true; }
bool DataBuffer::isSpecialActual() const { return false; }
void DataBuffer::showBufferLimited() {}
DataBuffer DataBuffer::dup() {
DataBuffer result;
result._dataType = _dataType;
result._lenInBytes = _lenInBytes;
result._primaryBuffer = _primaryBuffer;
result._specialBuffer = _specialBuffer;
result._isOwnerPrimary = _isOwnerPrimary;
result._isOwnerSpecial = _isOwnerSpecial;
result.allocateBuffers(true);
result.copyCounters(*this);
result.copyBufferFrom(*this);
return result;
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setSpecial(void* special, const bool isOwnerSpecial) {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setToZeroBuffers(const bool both) { memset(primary(), 0, getLenInBytes()); }
////////////////////////////////////////////////////////////////////////
void DataBuffer::syncToSpecial(const bool forceSync) {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::allocateSpecial() {}
////////////////////////////////////////////////////////////////////////
void DataBuffer::migrate() {}
template <typename T>
void _printHostBuffer(DataBuffer* buffer, long offset) {
sd::LongType len = buffer->getNumElements();
auto buff = buffer->template primaryAsT<T>();
sd::LongType limit = len;
if (limit == -1 || limit >= static_cast<LongType>(buffer->getNumElements())) {
limit = buffer->getNumElements();
}
const char* msg = nullptr;
if (msg != nullptr) {
printf("%s: ", msg);
} else {
printf("[");
}
sd::DataType dataType = buffer->getDataType();
auto baseOffset = offset;
if (dataType == sd::DataType::DOUBLE || dataType == sd::DataType::FLOAT32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (e > offset) printf(", ");
if (dataType == sd::DataType::DOUBLE) {
double val = static_cast<double>(buff[e]);
printf("%.15f",val);
} else {
printf("%.15f", static_cast<float>(buff[e]));
}
}
} else if (dataType == sd::DataType::INT64 || dataType == sd::DataType::UINT64 ||
dataType == sd::DataType::INT32 || dataType == sd::DataType::UINT32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (dataType == sd::DataType::INT64 || dataType == sd::DataType::UINT64) {
printf("%lld", static_cast<long long>(buff[e]));
} else {
printf("%d", static_cast<int>(buff[e]));
}
if (e < limit - 1) {
printf(", ");
}
}
} else if (dataType == sd::DataType::BOOL) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (static_cast<bool>(buff[e])) {
printf("true");
} else {
printf("false");
}
if (e < limit - 1) {
printf(", ");
}
}
} else if (dataType == sd::DataType::UTF8 || dataType == sd::DataType::UTF16 ||
dataType == sd::DataType::UTF32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
printf("\"%s\"", reinterpret_cast<const char*>(&buff[e]));
if (e < limit - 1) {
printf(", ");
}
}
}
printf("]\n");
fflush(stdout);
}
void DataBuffer::printHostDevice(long offset) {
auto xType = getDataType();
BUILD_SINGLE_SELECTOR(xType, _printHostBuffer,(this,offset),SD_COMMON_TYPES);
}
void DataBuffer::showCounters(const char* msg1, const char* msg2) {
}
template <typename T>
void* DataBuffer::primaryAtOffset(const LongType offset) {
if(_primaryBuffer == nullptr)
return nullptr;
T *type = reinterpret_cast<T*>(_primaryBuffer);
return reinterpret_cast<void *>(type + offset);
}
#define PRIMARYOFFSET(T) template void* DataBuffer::primaryAtOffset<GET_SECOND(T)>(const LongType offset);
ITERATE_LIST((SD_COMMON_TYPES),PRIMARYOFFSET)
template <typename T>
void* DataBuffer::specialAtOffset(const LongType offset) {
if(_specialBuffer == nullptr)
return nullptr;
T *type = reinterpret_cast<T*>(_specialBuffer);
return reinterpret_cast<void *>(type + offset);
}
#define SPECIALOFFSET(T) template void* DataBuffer::specialAtOffset<GET_SECOND(T)>(const LongType offset);
ITERATE_LIST((SD_COMMON_TYPES),SPECIALOFFSET)
} // namespace sd
+541
View File
@@ -0,0 +1,541 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
#ifndef NDARRAY_CPP
#define NDARRAY_CPP
#include <array/NDArray.h>
#include <helpers/ArrayUtils.h>
#include <helpers/ConstantTadHelper.h>
#include <helpers/ShapeUtils.h>
#include <helpers/logger.h>
#include <indexing/NDIndex.h>
#include <loops/BroadcastPairwiseConverter.h>
#include <loops/random.h>
#include <ops/ops.h>
#include <array/NDArray.hXX>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <system/selective_rendering.h>
namespace sd {
// NDArray implementation for .cpp file
void NDArray::printBufferDebug(const char* msg, sd::LongType offset, sd::LongType limit) {
if (msg) sd_printf("%s:\n", msg);
if(limit < 0) limit = lengthOf();
// Print array info
sd_printf("NDArray: Shape=[", 0);
for (int i = 0; i < rankOf(); i++) {
sd_printf("%lld", (long long)sizeAt(i));
if (i < rankOf() - 1) sd_printf(",", 0);
}
sd_printf("], DataType=%s, EWS=%lld, Order=%c\n",
DataTypeUtils::asString(dataType()).c_str(), (long long)ews(), ordering());
// Print buffer state
if (_buffer != nullptr) {
_buffer->printBufferDebug("Buffer contents", offset, limit);
} else {
sd_printf("Buffer is nullptr\n", 0);
}
}
////////////////////////////////////////////////////////////////////////
void* NDArray::platformBuffer() { return buffer(); }
////////////////////////////////////////////////////////////////////////
template <typename T>
void NDArray::fillAsTriangular(const float val, int lower, int upper, NDArray& target, const char direction, const bool includeEdges) {
if (isS()) THROW_EXCEPTION("NDArray::fillArrayAsTriangular: you can't use this method on String array!");
if (!isSameShape(target) &&
!(rankOf() == 1 && target.rankOf() == 2 && sizeAt(0) == target.sizeAt(0) && sizeAt(0) == target.sizeAt(1)))
THROW_EXCEPTION("NDArray::fillArrayAsTriangular method: wrong shape of target array !");
const T value = static_cast<T>(val);
const auto x = reinterpret_cast<const T*>(buffer());
auto z = reinterpret_cast<T*>(target.buffer());
const int xRank = rankOf();
const int zRank = target.rankOf();
const auto zLen = target.lengthOf();
const bool areSameOffsets = shape::haveSameShapeAndStrides(shapeInfo(), target.shapeInfo());
sd::LongType *targetShape = shape::shapeOf(target.shapeInfo());
sd::LongType *targetStride = shape::stride(target.shapeInfo());
sd::LongType targetRank = target.rankOf();
sd::LongType *xShape = shape::shapeOf(shapeInfo());
sd::LongType *xStride = shape::stride(shapeInfo());
sd::LongType thisRank = this->rankOf();
auto func = PRAGMA_THREADS_FOR {
sd::LongType coords[SD_MAX_RANK], temp;
sd::LongType vectorCoord[1] = {0};
bool notVectorScalar = targetRank == 2 && thisRank == 2;
bool thisNotVectorScalar = !shape::isScalar(this->shapeInfo()) && !shape::isVector(this->shapeInfo());
bool targetNotVectorScalar = !shape::isScalar(target.shapeInfo()) && !shape::isVector(target.shapeInfo());
for (sd::LongType i = start; i < stop; i++) {
INDEX2COORDS(i, targetRank,targetShape, coords);
sd::LongType row = targetNotVectorScalar ? coords[zRank - 2] : 0;
sd::LongType col = targetNotVectorScalar ? coords[zRank - 1] : 1;
sd::LongType zOffset, xOffset;
if (target.rankOf() < 2) {
COORDS2INDEX(targetRank, targetStride, vectorCoord, zOffset);
} else {
COORDS2INDEX(targetRank, targetStride, coords, zOffset);
}
if (!areSameOffsets && rankOf() < 2) {
COORDS2INDEX(thisRank, xStride, vectorCoord, xOffset);
} else if (areSameOffsets) {
xOffset = zOffset;
} else {
COORDS2INDEX(thisRank, xStride, coords, xOffset);
}
bool rowExclusive = this->rankOf() == target.rankOf();
bool colExclusive = this->rankOf() == target.rankOf();
auto lCompare = includeEdges ? row <= (col - lower) : row < (col - lower);
auto uCompare = includeEdges ? row >= (col - upper) : row > (col - upper);
if ((direction == 'u' && lCompare) || (direction == 'l' && uCompare)) {
z[zOffset] = value;
} else {
z[zOffset] = x[xOffset];
}
if (this != &target) {
if (xRank != zRank) {
temp = coords[0];
coords[0] = coords[1];
}
if (xRank != zRank) // restore first coordinate
coords[0] = temp;
}
if (vectorCoord[0] == this->lengthOf() - 1) {
vectorCoord[0] = 0;
} else {
vectorCoord[0] = vectorCoord[0] + 1;
}
}
};
samediff::Threads::parallel_for(func, 0, zLen);
}
BUILD_SINGLE_TEMPLATE( void NDArray::fillAsTriangular,
(const float val, int lower, int upper, NDArray& target, const char direction,const bool includeEdges), SD_COMMON_TYPES);
////////////////////////////////////////////////////////////////////////
void NDArray::setIdentity() {
if (isS()) THROW_EXCEPTION("NDArray::setIdentity: you can't use this method on String array!");
this->nullify();
int rank = rankOf();
auto shape = shapeOf();
int minDim = SD_MAX_INT;
sd::LongType indices[SD_MAX_RANK];
for (int j = 0; j < rank; ++j) indices[j] = 1;
sd::LongType offset;
COORDS2INDEX(rank, shape::stride(shapeInfo()), indices, offset);
for (int i = 0; i < rank; ++i)
if (minDim > shape[i]) minDim = shape[i];
float v = 1.0f;
for (int i = 0; i < minDim; ++i) templatedSet<float,float>(buffer(), i * offset, this->dataType(), &v);
}
////////////////////////////////////////////////////////////////////////
template <typename T>
static void templatedSwap(void* xBuffer, void* yBuffer, const sd::LongType* xShapeInfo, const sd::LongType* yShapeInfo,
sd::LongType length) {
auto x = reinterpret_cast<T*>(xBuffer);
auto y = reinterpret_cast<T*>(yBuffer);
const bool isSameOrders = shape::order(xShapeInfo) == shape::order(xShapeInfo);
sd::LongType xRank = shape::rank(xShapeInfo);
sd::LongType yRank = shape::rank(yShapeInfo);
sd::LongType *xShape = shape::shapeOf(xShapeInfo);
sd::LongType *yShape = shape::shapeOf(yShapeInfo);
sd::LongType *xStride = shape::stride(xShapeInfo);
sd::LongType *yStride = shape::stride(yShapeInfo);
auto func = PRAGMA_THREADS_FOR {
if (isSameOrders) {
for (sd::LongType i = start; i < stop; i++) {
LongType xCoords[SD_MAX_RANK];
LongType yCoords[SD_MAX_RANK];
LongType xOffset;
LongType yOffset;
INDEX2COORDS(i, xRank, xShape, xCoords);
COORDS2INDEX(xRank, shape::stride(xShapeInfo), xCoords, xOffset);
INDEX2COORDS(i, yRank,yShape, yCoords);
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
sd::math::sd_swap(x[xOffset], y[yOffset]);
}
} else if (shape::haveSameShapeAndStrides(xShapeInfo, yShapeInfo)) {
for (sd::LongType i = start; i < stop; i++) {
LongType coords[SD_MAX_RANK];
LongType ind;
INDEX2COORDS(i, xRank, xShape, coords);
COORDS2INDEX(xRank, xStride, coords, ind);
sd::math::sd_swap(x[ind], y[ind]);
}
} else {
for (sd::LongType i = start; i < stop; i++) {
LongType xCoords[SD_MAX_RANK];
LongType yCoords[SD_MAX_RANK];
LongType xInd;
LongType yInd;
INDEX2COORDS(i, xRank, xShape, xCoords);
COORDS2INDEX(xRank, xStride, xCoords, xInd);
INDEX2COORDS(i, yRank, yShape, yCoords);
COORDS2INDEX(yRank, yStride, yCoords, yInd);
sd::math::sd_swap(x[xInd], y[yInd]);
}
}
};
samediff::Threads::parallel_for(func, 0, length);
}
BUILD_SINGLE_TEMPLATE( void templatedSwap,
(void* xBuffer, void* yBuffer, const sd::LongType* xShapeInfo, const sd::LongType* yShapeInfo,
sd::LongType length),
SD_COMMON_TYPES);
////////////////////////////////////////////////////////////////////////
void NDArray::swapUnsafe(NDArray& other) {
auto xType = this->dataType();
if (xType != other.dataType())
THROW_EXCEPTION("NDArray::swapUnsage method: both arrays must have the same data type");
if (buffer() == nullptr || other.buffer() == nullptr)
THROW_EXCEPTION("NDArray::swapUnsafe method: input array should not be empty!");
if (lengthOf() != other.lengthOf())
THROW_EXCEPTION("NDArray::swapUnsafe method: input arrays should have the same length!");
BUILD_SINGLE_SELECTOR(xType, templatedSwap,
(buffer(), other.buffer(), shapeInfo(), other.shapeInfo(), this->lengthOf()), SD_COMMON_TYPES);
}
////////////////////////////////////////////////////////////////////////
void NDArray::synchronize(const char* msg) {
// no-op
}
void NDArray::syncToDevice() {}
void NDArray::syncToHost() {}
void NDArray::tickWriteHost() {}
void NDArray::tickWriteDevice() {}
void NDArray::tickReadHost() {}
void NDArray::tickReadDevice() {}
void NDArray::tickBothActual() {}
bool NDArray::isActualOnHostSide() { return true; }
bool NDArray::isActualOnDeviceSide() { return true; }
void NDArray::makeBothBuffersActual() {}
void NDArray::preparePrimaryUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList, bool synchronizeWritables) {
// no-op
}
void NDArray::registerPrimaryUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList) {
// no-op
}
void NDArray::prepareSpecialUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList, bool synchronizeWritables) {
// no-op
}
void NDArray::registerSpecialUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList) {
// no-op
}
void NDArray::syncShape() {
// no-op
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void NDArray::printCurrentBuffer(const bool host, const char* msg, const int precision) {}
template void NDArray::printCurrentBuffer<int>(const bool host, const char* msg, const int precision) ;
template void NDArray::printCurrentBuffer<float>(const bool host, const char* msg, const int precision) ;
template void NDArray::printCurrentBuffer<double>(const bool host, const char* msg, const int precision);
template void NDArray::printCurrentBuffer<sd::LongType>(const bool host, const char* msg, const int precision) ;
////////////////////////////////////////////////////////////////////////
void* NDArray::specialBuffer() {
if (_buffer == nullptr) {
THROW_EXCEPTION("NDArray::specialBuffer(): _buffer is nullptr - array not properly initialized");
}
void* specialBuf = _buffer->special();
// On CPU, special buffer is nullptr (only used for GPU/CUDA) - this is expected and normal
if (specialBuf == nullptr) {
return nullptr;
}
return static_cast<int8_t*>(specialBuf) + (_offset * sizeOfT());
}
//////////////////////////////////////////////////////////////////////////
// change an array by repeating it the number of times given by reps.
NDArray NDArray::tile(const std::vector<sd::LongType>& reps) {
const int repsSize = reps.size();
sd::LongType product = 1;
for (const auto& item : reps) product *= item;
if (product == 0) THROW_EXCEPTION("NDArray::tile method: one of the elements in reps array is zero !");
int rankOld = rankOf();
int diff = rankOld - repsSize;
if (product == 1) { // in this case 2 possibilities are present: just reshape or nothing to do
NDArray result(*this);
if (diff < 0) { // reshape to higher dimension
std::vector<sd::LongType> shapeNew =
reps; // there is requirement to have unities at first "diff" positions of new shape
memcpy(&shapeNew[-diff], result.shapeInfo() + 1,
rankOld * sizeof(sd::LongType)); // put old shape numbers at rest of positions
result.reshapei(ordering(), shapeNew);
}
return result; // nothing to do, if diff >= 0 -> identity tile
}
// evaluate shapeInfo for resulting array
auto newShapeInfo = ShapeUtils::evalTileShapeInfo(*this, reps, getContext()->getWorkspace());
// create new buffer, in any case the memory amount new buffer points to is bigger then those for old _buffer
DataBuffer * newBuff =
new DataBuffer(shape::length(newShapeInfo) * sizeOfT(), dataType(), getContext()->getWorkspace());
// assign new shape and new buffer to resulting array
NDArray result(newBuff,newShapeInfo , getContext());
// fill newBuff, loop through all elements of newBuff
// looping through _buffer goes automatically by means of getSubArrayIndex applying
const auto resultLen = result.lengthOf();
auto xType = this->dataType();
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto xOffset = result.getOffset(i);
auto yOffset = shape::subArrayOffset(i, newShapeInfo, shapeInfo());
BUILD_SINGLE_SELECTOR(xType, this->template templatedAssign,
(result.buffer(), xOffset, this->buffer(), yOffset), SD_COMMON_TYPES);
}
};
samediff::Threads::parallel_for(func, 0, resultLen);
result.tickWriteHost();
return result;
}
//////////////////////////////////////////////////////////////////////////
// change an array by repeating it the number of times given by reps.
void NDArray::tile(const std::vector<LongType>& reps, NDArray& target) {
// Validate the tile operation
auto repProd = shape::prodLong(reps.data(), reps.size());
if (repProd < 1)
THROW_EXCEPTION("NDArray::tile: reps can't contain 0s");
// Validate the target shape
auto correctShapeInfo = ShapeUtils::evalTileShapeInfo(*this, reps, getContext()->getWorkspace());
if (!shape::equalsSoft(correctShapeInfo, target.shapeInfo())) {
THROW_EXCEPTION("NDArray::tile method - shapeInfo of target array is not suitable for tile operation!");
}
const auto targetLen = target.lengthOf();
// Safely calculate source array offset
for (LongType i = 0; i < targetLen; ++i) {
// Calculate target array offset
auto xOffset = target.getOffset(i);
// Calculate source coordinates based on target coordinates
LongType targetCoords[SD_MAX_RANK];
INDEX2COORDS(i, shape::rank(target.shapeInfo()), shape::shapeOf(target.shapeInfo()), targetCoords);
// Map target coordinates to source coordinates manually
LongType sourceCoords[SD_MAX_RANK];
for (int d = 0; d < shape::rank(shapeInfo()); d++) {
// Apply modulo for each dimension
sourceCoords[d] = targetCoords[d] % shape::sizeAt(shapeInfo(), d);
}
// Calculate source offset from source coordinates
LongType sourceOffset;
COORDS2INDEX(shape::rank(shapeInfo()), shape::stride(shapeInfo()), sourceCoords, sourceOffset);
auto targetDataType = target.dataType();
auto selfDType = dataType();
// Copy the value
BUILD_DOUBLE_SELECTOR(target.dataType(), dataType(), templatedDoubleAssign,
(target.buffer(), xOffset, buffer(), sourceOffset), SD_COMMON_TYPES, SD_COMMON_TYPES);
}
}
//////////////////////////////////////////////////////////////////////////
void NDArray::tile(NDArray& target) {
if (rankOf() > target.rankOf())
THROW_EXCEPTION(
"NDArray::tile method - rank of target array must be bigger or equal to the rank of this array !");
if (!ShapeUtils::areShapesBroadcastable(*this, target))
THROW_EXCEPTION("NDArray::tile method - shapeInfo of target array is not suitable for tile operation !");
// fill newBuff, loop through all elements of newBuff
// looping through _buffer goes automatically by means of getSubArrayIndex applying
const auto ews = target.ews();
const auto targetLen = target.lengthOf();
if (target.ordering() == 'c' && ews >= 1) {
for (sd::LongType i = 0; i < targetLen; ++i) {
auto yOffset = shape::subArrayOffset(i, target.shapeInfo(), shapeInfo());
auto targetDataType = target.dataType();
auto selfDType = dataType();
BUILD_DOUBLE_SELECTOR(target.dataType(), dataType(), templatedDoubleAssign,
(target.buffer(), i * ews, buffer(), yOffset), SD_COMMON_TYPES, SD_COMMON_TYPES);
}
} else {
for (sd::LongType i = 0; i < targetLen; ++i) {
auto xOffset = target.getOffset(i);
auto yOffset = shape::subArrayOffset(i, target.shapeInfo(), shapeInfo());
auto targetDataType = target.dataType();
auto selfDType = dataType();
BUILD_DOUBLE_SELECTOR(target.dataType(), dataType(), templatedDoubleAssign,
(target.buffer(), xOffset, buffer(), yOffset), SD_COMMON_TYPES, SD_COMMON_TYPES);
}
}
}
////////////////////////////////////////////////////////////////////////
template <typename X, typename Z>
static void repeat_(NDArray& input, NDArray& output, const std::vector<LongType>& repeats, const LongType axis) {
const X* x = input.bufferAsT<X>();
Z* z = output.bufferAsT<Z>();
const sd::LongType rank = input.rankOf(); // xRank = zRank
const sd::LongType zLen = output.lengthOf(); // xLen <= zLen
const sd::LongType repSize = repeats.size();
sd::LongType outputRank = output.rankOf();
sd::LongType* outputShape = shape::shapeOf(output.shapeInfo());
sd::LongType* outputStride = shape::stride(output.shapeInfo());
sd::LongType inputRank = input.rankOf();
sd::LongType* inputShape = shape::shapeOf(input.shapeInfo());
sd::LongType* inputStride = shape::stride(input.shapeInfo());
// loop through input array
auto func = PRAGMA_THREADS_FOR {
sd::LongType coords[SD_MAX_RANK], temp;
for (sd::LongType i = start; i < stop; i++) {
INDEX2COORDS(i, outputRank, outputShape, coords);
sd::LongType zOffset;
COORDS2INDEX(outputRank, outputStride, coords, zOffset);
temp = coords[axis];
if (repSize > 1) {
for (sd::LongType j = 0; j < repSize; ++j) {
coords[axis] -= repeats[j];
if (coords[axis] < 0) {
coords[axis] = j;
break;
}
}
} else
coords[axis] /= repeats[0];
sd::LongType xOffset;
COORDS2INDEX(inputRank,inputStride, coords, xOffset);
z[zOffset] = x[xOffset];
coords[axis] = temp;
}
};
samediff::Threads::parallel_for(func, 0, zLen);
}
//////////////////////////////////////////////////////////////////////////
// create new array by repeating it the number of times given by repeats
NDArray NDArray::repeat(const int axis, const std::vector<LongType>& repeats) {
NDArray *thisArr = const_cast<NDArray*>(this);
std::vector<sd::LongType> repeatShape = ShapeUtils::evalRepeatShape(axis, repeats, *thisArr);
NDArray output('c',repeatShape, dataType(), getContext());
BUILD_SINGLE_SELECTOR_TWICE(dataType(), repeat_, (*this, output, repeats, axis), SD_COMMON_TYPES);
return output;
}
//////////////////////////////////////////////////////////////////////////
// fill array by repeating it the number of times given by reps
void NDArray::repeat(const int axis, const std::vector<LongType>& repeats, NDArray& target) {
NDArray *thisArr = const_cast<NDArray*>(this);
if (!target.isSameShape(ShapeUtils::evalRepeatShape(axis, repeats, *thisArr)))
THROW_EXCEPTION(
"NDArray::repeat(const int axis, const std::vector<int>& repeats, NDArray& target) method: wrong shape of "
"target array!");
auto targetDataType = target.dataType();
auto selfDType = dataType();
BUILD_DOUBLE_SELECTOR(dataType(), target.dataType(), repeat_, (*this, target, repeats, axis), SD_COMMON_TYPES,
SD_COMMON_TYPES);
}
//////////////////////////////////////////////////////////////////////////
#ifndef __JAVACPP_HACK__
#include "NDArrayLambda.hpp"
#endif
} // namespace sd
#endif
+149
View File
@@ -0,0 +1,149 @@
################################################################################
#
#
# 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
################################################################################
#ifndef NDARRAY_MACRO
#define NDARRAY_MACRO
#include <op_boilerplate.h>
//NDArray<T> *other, T *extraParams
BUILD_CALL_1(template void NDArray<float>::template applyPairwiseTransform, float, (NDArray<float>* other, float* extraParams), PAIRWISE_TRANSFORM_OPS)
BUILD_CALL_1(template void NDArray<float16>::applyPairwiseTransform, float16, (NDArray<float16>* other, float16* extraParams), PAIRWISE_TRANSFORM_OPS)
BUILD_CALL_1(template void NDArray<double>::applyPairwiseTransform, double, (NDArray<double>* other, double* extraParams), PAIRWISE_TRANSFORM_OPS)
// NDArray<T> *other, NDArray<T> *target, T *extraParams
BUILD_CALL_1(template void sd::NDArray<float>::applyPairwiseTransform, float, (NDArray<float>* other, NDArray<float>* target, float* extraParams), PAIRWISE_TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyPairwiseTransform, float16, (NDArray<float16>* other, NDArray<float16>* target, float16* extraParams), PAIRWISE_TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyPairwiseTransform, double, (NDArray<double>* other, NDArray<double>* target, double* extraParams), PAIRWISE_TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyScalar, float16, (NDArray<float16>& scalar, NDArray<float16>* target, float16 *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyScalar, float16, (float16 scalar, NDArray<float16>* target, float16 *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyScalar, float, (NDArray<float>& scalar, NDArray<float>* target, float *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyScalar, float, (float scalar, NDArray<float>* target, float *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyScalar, double, (NDArray<double>& scalar, NDArray<double>* target, double *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyScalar, double, (double scalar, NDArray<double>* target, double *extraParams) const, SCALAR_OPS)
BUILD_CALL_1(template float16 sd::NDArray<float16>::reduceNumber, float16, (float16 *extraParams) const, REDUCE_OPS)
BUILD_CALL_1(template float sd::NDArray<float>::reduceNumber, float, (float *extraParams) const, REDUCE_OPS)
BUILD_CALL_1(template double sd::NDArray<double>::reduceNumber, double, (double *extraParams) const, REDUCE_OPS)
BUILD_CALL_1(template LongType sd::NDArray<float16>::indexReduceNumber, float16, (float16 *extraParams), INDEX_REDUCE_OPS)
BUILD_CALL_1(template LongType sd::NDArray<float>::indexReduceNumber, float, (float *extraParams), INDEX_REDUCE_OPS)
BUILD_CALL_1(template LongType sd::NDArray<double>::indexReduceNumber, double, (double *extraParams), INDEX_REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyBroadcast, float16, (std::initializer_list<int> list, const sd::NDArray<float16>* a, sd::NDArray<float16>* b, float16* c), BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyBroadcast, float, (std::initializer_list<int> list, const sd::NDArray<float>* a, sd::NDArray<float>* b, float* c), BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyBroadcast, double, (std::initializer_list<int> list, const sd::NDArray<double>* a, sd::NDArray<double>* b, double* c), BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyTrueBroadcast, float16,(const sd::NDArray<float16>* a, sd::NDArray<float16>* target, const bool checkTargetShape, float16* c) const, BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyTrueBroadcast, float, (const sd::NDArray<float>* a, sd::NDArray<float>* target, const bool checkTargetShape, float* c) const, BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyTrueBroadcast, double, (const sd::NDArray<double>* a, sd::NDArray<double>* target, const bool checkTargetShape, double* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<float16>* sd::NDArray<float16>::applyTrueBroadcast, float16, (const sd::NDArray<float16>* a, float16* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<float>* sd::NDArray<float>::applyTrueBroadcast, float, (const sd::NDArray<float>* a, float* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<double>* sd::NDArray<double>::applyTrueBroadcast, double, (const sd::NDArray<double>* a, double* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<float16> sd::NDArray<float16>::applyTrueBroadcast, float16, (const sd::NDArray<float16>& a, float16* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<float> sd::NDArray<float>::applyTrueBroadcast, float, (const sd::NDArray<float>& a, float* c) const, BROADCAST_OPS)
BUILD_CALL_1(template sd::NDArray<double> sd::NDArray<double>::applyTrueBroadcast, double, (const sd::NDArray<double>& a, double* c) const, BROADCAST_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyTransform, float16, (NDArray<float16>* target, float16* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyTransform, float, (NDArray<float>* target, float* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyTransform, double, (NDArray<double>* target, double* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyTransform, float16, (float16* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyTransform, float, (float* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyTransform, double, (double* extraParams), TRANSFORM_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::applyRandom, float16, (sd::random::RandomBuffer *buffer, NDArray<float16>* y, NDArray<float16>* z, float16* extraParams), RANDOM_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::applyRandom, float, (sd::random::RandomBuffer *buffer, NDArray<float>* y, NDArray<float>* z, float* extraParams), RANDOM_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::applyRandom, double, (sd::random::RandomBuffer *buffer, NDArray<double>* y, NDArray<double>* z, double* extraParams), RANDOM_OPS)
BUILD_CALL_1(template NDArray<float16> sd::NDArray<float16>::transform, float16, (float16* extraParams) const, TRANSFORM_OPS)
BUILD_CALL_1(template NDArray<float> sd::NDArray<float>::transform, float, (float* extraParams) const, TRANSFORM_OPS)
BUILD_CALL_1(template NDArray<double> sd::NDArray<double>::transform, double, (double* extraParams) const, TRANSFORM_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template reduceAlongDimension, float, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template reduceAlongDimension, float16, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template reduceAlongDimension, double, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float> sd::NDArray<float>::template reduceAlongDims, float, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float16> sd::NDArray<float16>::template reduceAlongDims, float16, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<double> sd::NDArray<double>::template reduceAlongDims, double, (const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template reduceAlongDimension, float, (const std::initializer_list<int>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template reduceAlongDimension, float16, (const std::initializer_list<int>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template reduceAlongDimension, double, (const std::initializer_list<int>& dimensions, const bool keepDims, const bool supportOldShapes) const, REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::template reduceAlongDimension, float, (NDArray<float>* target, const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes, float * extras) const, REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::template reduceAlongDimension, float16, (NDArray<float16>* target, const std::vector<LongType>& dimensions, const bool keepDims, const bool supportOldShapes, float16 * extras) const, REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::template reduceAlongDimension, double, (NDArray<double>* target, const std::vector<int>& dimension, const bool keepDims, const bool supportOldShapes, double * extras) const, REDUCE_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template varianceAlongDimension, float, (const bool biasCorrected, const std::initializer_list<int>& dimensions) const, SUMMARY_STATS_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template varianceAlongDimension, float16, (const bool biasCorrected, const std::initializer_list<int>& dimensions) const, SUMMARY_STATS_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template varianceAlongDimension, double, (const bool biasCorrected, const std::initializer_list<int>& dimensions) const, SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::template varianceAlongDimension, float, (const NDArray<float> *target, const bool biasCorrected, const std::initializer_list<int>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::template varianceAlongDimension, float16, (const NDArray<float16> *target,const bool biasCorrected, const std::initializer_list<int>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::template varianceAlongDimension, double, (const NDArray<double> *target, const bool biasCorrected, const std::initializer_list<int>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::template varianceAlongDimension, float, (const NDArray<float> *target, const bool biasCorrected, const std::vector<LongType>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::template varianceAlongDimension, float16, (const NDArray<float16> *target,const bool biasCorrected, const std::vector<LongType>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::template varianceAlongDimension, double, (const NDArray<double> *target, const bool biasCorrected, const std::vector<LongType>& dimensions), SUMMARY_STATS_OPS)
BUILD_CALL_1(template float sd::NDArray<float>::template varianceNumber, float, (bool biasCorrected), SUMMARY_STATS_OPS)
BUILD_CALL_1(template float16 sd::NDArray<float16>::template varianceNumber, float16, (bool biasCorrected), SUMMARY_STATS_OPS)
BUILD_CALL_1(template double sd::NDArray<double>::template varianceNumber, double, (bool biasCorrected), SUMMARY_STATS_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template applyReduce3, float, (const NDArray<float>* other, const float* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template applyReduce3, float16, (const NDArray<float16>* other, const float16* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template applyReduce3, double, (const NDArray<double>* other, const double* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template applyReduce3, float, (const NDArray<float>* other, const std::vector<int> &dims, const float* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template applyReduce3, float16, (const NDArray<float16>* other, const std::vector<int> &dims, const float16* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template applyReduce3, double, (const NDArray<double>* other, const std::vector<int> &dims, const double* extraParams) const, REDUCE3_OPS)
BUILD_CALL_1(template void sd::NDArray<float>::template applyIndexReduce, float, (const NDArray<float>* target, const std::vector<int> & alpha, const float* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<float16>::template applyIndexReduce, float16, (const NDArray<float16>* target, const std::vector<int> & alpha, const float16* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template void sd::NDArray<double>::template applyIndexReduce, double, (const NDArray<double>* target, const std::vector<int> & alpha, const double* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template applyIndexReduce, float, (const std::vector<int> & alpha, const float* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template applyIndexReduce, float16, (const std::vector<int> & alpha, const float16* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template applyIndexReduce, double, (const std::vector<int> & alpha, const double* beta) const, INDEX_REDUCE_OPS)
BUILD_CALL_1(template NDArray<float> *sd::NDArray<float>::template applyAllReduce3, float, (const sd::NDArray<float>* alpha, const std::vector<int> & beta, float const* gamma) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<float16> *sd::NDArray<float16>::template applyAllReduce3, float16, (const sd::NDArray<float16>* alpha, const std::vector<int> & beta, float16 const* gamma) const, REDUCE3_OPS)
BUILD_CALL_1(template NDArray<double> *sd::NDArray<double>::template applyAllReduce3, double, (const sd::NDArray<double>* alpha, const std::vector<int> & beta, double const* gamma) const, REDUCE3_OPS)
template NDArray<float> mmul(const NDArray<float>& left, const NDArray<float>& right);
template NDArray<float16> mmul(const NDArray<float16>& left, const NDArray<float16>& right);
template NDArray<double> mmul(const NDArray<double>& left, const NDArray<double>& right);
// template NDArray<float> operator-(const float, const NDArray<float>&);
// template NDArray<float16> operator-(const float16, const NDArray<float16>&);
// template NDArray<double> operator-(const double, const NDArray<double>&);
// template NDArray<float> operator+(const float, const NDArray<float>&);
// template NDArray<float16> operator+(const float16, const NDArray<float16>&);
// template NDArray<double> operator+(const double, const NDArray<double>&);
#endif
+559
View File
@@ -0,0 +1,559 @@
/*
* ******************************************************************************
* *
* *
* * 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
* *****************************************************************************
*/
template <typename T>
SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third,
std::function<T(T, T, T)>& func, NDArray* target) {
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyTriplewiseLambda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != second->dataType() || dataType() != third->dataType() || dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyTriplewiseLambda<T> method: bother four arrays (this, second, third, target) should have the "
"same type !");
if (this->lengthOf() != second->lengthOf() || this->lengthOf() != third->lengthOf() || !this->isSameShape(second) ||
!this->isSameShape(third)) {
std::string errorMessage;
errorMessage += "applyTriplewiseLambda requires all operands to have the same shape\n";
errorMessage += "this shape: " + ShapeUtils::shapeAsString(this->shapeInfo()) + "\n";
errorMessage += "second shape: " + ShapeUtils::shapeAsString(second->shapeInfo()) + "\n";
errorMessage += "third shape: " + ShapeUtils::shapeAsString(third->shapeInfo()) + "\n";
errorMessage += "target shape: " + ShapeUtils::shapeAsString(target->shapeInfo()) + "\n";
THROW_EXCEPTION(errorMessage.c_str());
}
auto f = this->bufferAsT<T>();
auto s = second->bufferAsT<T>();
auto t = third->bufferAsT<T>();
auto z = target->bufferAsT<T>();
if (f == z) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto tOffset = this->getOffset(e);
auto uOffset = second->getOffset(e);
auto vOffset = third->getOffset(e);
f[tOffset] = func(f[tOffset], s[uOffset], t[vOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto tOffset = this->getOffset(e);
auto uOffset = second->getOffset(e);
auto vOffset = third->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func(f[tOffset], s[uOffset], t[vOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
}
}
#if defined(HAS_DOUBLE)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third,
std::function<double(double, double, double)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT32)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third,
std::function<float(float, float, float)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT16)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<float16(float16, float16, float16)>& func, NDArray* target);
#endif
#if defined(HAS_BFLOAT16)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<bfloat16(bfloat16, bfloat16, bfloat16)>& func,
NDArray* target);
#endif
#if defined(HAS_INT64)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<sd::LongType(sd::LongType, sd::LongType, sd::LongType)>& func,
NDArray* target);
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<long(long, long, long)>& func,
NDArray* target);
#endif
#if defined(HAS_INT32)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third,
std::function<int(int, int, int)>& func,
NDArray* target);
#endif
#if defined(HAS_INT16)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<int16_t(int16_t, int16_t, int16_t)>& func, NDArray* target);
#endif
#if defined(HAS_INT8)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<char(char, char, char)>& func, NDArray* target);
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<signed char(signed char, signed char, signed char)>& func, NDArray* target);
#endif
#if defined(HAS_UINT8)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<uint8_t(uint8_t, uint8_t, uint8_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT16)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<uint16_t(uint16_t, uint16_t, uint16_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT32)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<uint32_t(uint32_t, uint32_t, uint32_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT64)
template SD_LIB_HIDDEN void NDArray::applyTriplewiseLambda(
NDArray* second, NDArray* third, std::function<uint64_t(uint64_t, uint64_t, uint64_t)>& func,
NDArray* target);
#endif
//////////////////////////////////////////////////////////////////////////
template <typename T>
SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other, std::function<T(T, T)>& func,
NDArray* target) {
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyPairwiseLambda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != other->dataType() || dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyPairwiseLambda<T> method: all three arrays (this, other, target) must have the same type !");
// scalar is broadcastable
if (this->lengthOf() != other->lengthOf() && !this->isScalar() && !other->isScalar()) {
THROW_EXCEPTION("applyPairwiseLambda requires both operands to have the same shape");
}
auto f = this->bufferAsT<T>();
auto s = other->bufferAsT<T>();
auto z = target->bufferAsT<T>();
auto isTargetOrderEws = !isView() && !target->isView() && this->ordering() == target->ordering() && (this->ews() == 1 && target->ews() == 1);
if (other->isScalar()) {
auto otherVal = s[other->getOffset(0)];
if (isTargetOrderEws) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) z[e] = func(f[e], otherVal);
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
if (f == z) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
f[xOffset] = func(f[xOffset], otherVal);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func(f[xOffset], otherVal);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
}
}
}
if (f == z && !this->isView() && !other->isView() && this->ordering() == other->ordering()) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto yOffset = other->getOffset(e);
f[xOffset] = func(f[xOffset], s[yOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto yOffset = other->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func(f[xOffset], s[yOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
}
}
#if defined(HAS_DOUBLE)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<double(double, double)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT32)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<float(float, float)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT16)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<float16(float16, float16)>& func,
NDArray* target);
#endif
#if defined(HAS_BFLOAT16)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<bfloat16(bfloat16, bfloat16)>& func,
NDArray* target);
#endif
#if defined(HAS_INT64)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(
NDArray* other, std::function<sd::LongType(sd::LongType, sd::LongType)>& func, NDArray* target);
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(
NDArray* other, std::function<long(long, long)>& func, NDArray* target);
#endif
#if defined(HAS_INT32)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other, std::function<int(int, int)>& func,
NDArray* target);
#endif
#if defined(HAS_INT16)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<int16_t(int16_t, int16_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT8)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<uint8_t(uint8_t, uint8_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT16)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<uint16_t(uint16_t, uint16_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT32)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<uint32_t(uint32_t, uint32_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UNSIGNEDLONG)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<uint64_t(uint64_t, uint64_t)>& func,
NDArray* target);
#endif
#if defined(HAS_INT8)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<int8_t(int8_t, int8_t)>& func,
NDArray* target);
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<SignedChar(SignedChar, SignedChar)>& func,
NDArray* target);
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<char(char, char)>& func,
NDArray* target);
#endif
#if defined(HAS_BOOL)
template SD_LIB_HIDDEN void NDArray::applyPairwiseLambda(NDArray* other,
std::function<bool(bool, bool)>& func, NDArray* target);
#endif
//////////////////////////////////////////////////////////////////////////
template <typename T>
SD_LIB_HIDDEN void NDArray::applyLambda(std::function<T(T)>& func, NDArray* target) {
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyLambda<T> method: wrong template parameter T, its type should be the same as type of this "
"array!");
if (dataType() != target->dataType())
THROW_EXCEPTION("NDArray::applyLambda<T> method: types of this and target array should match !");
auto f = this->bufferAsT<T>();
auto z = target->bufferAsT<T>();
if (f == z) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e+= increment) {
auto xOffset = this->getOffset(e);
f[xOffset] = func(f[xOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length,1);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e+= increment) {
auto xOffset = this->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func(f[xOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length,1);
}
}
#if defined(HAS_DOUBLE)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<double(double)>& func, NDArray* target);
#endif
#if defined(HAS_FLOAT32)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<float(float)>& func, NDArray* target);
#endif
#if defined(HAS_FLOAT16)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<float16(float16)>& func, NDArray* target);
#endif
#if defined(HAS_BFLOAT16)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<bfloat16(bfloat16)>& func, NDArray* target);
#endif
#if defined(HAS_INT64)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<sd::LongType(sd::LongType)>& func,
NDArray* target);
#endif
#if defined(HAS_INT16)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<int16_t(int16_t)>& func, NDArray* target);
#endif
#if defined(HAS_INT32)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<int32_t(int32_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT8)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<uint8_t(uint8_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT16)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<uint16_t(uint16_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT32)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<uint32_t(uint32_t)>& func, NDArray* target);
#endif
#if defined(HAS_UNSIGNEDLONG)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<uint64_t(uint64_t)>& func, NDArray* target);
#endif
#if defined(HAS_INT8)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<int8_t(int8_t)>& func, NDArray* target);
#endif
#if defined(HAS_BOOL)
template SD_LIB_HIDDEN void NDArray::applyLambda(std::function<bool(bool)>& func, NDArray* target);
#endif
//////////////////////////////////////////////////////////////////////////
template <typename T>
SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<T(sd::LongType, T)>& func, NDArray* target) {
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyIndexedLambda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != target->dataType())
THROW_EXCEPTION("NDArray::applyIndexedLambda<T> method: types of this and target array should match !");
auto f = this->bufferAsT<T>();
auto z = target->bufferAsT<T>();
if (this->ordering() == target->ordering() && (this->ews() == 1 && target->ews() == 1)) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) z[e] = func(e, f[e]);
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
if (f == z) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
f[xOffset] = func(e, f[xOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func(e, f[xOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
}
}
}
#if defined(HAS_DOUBLE)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<double(sd::LongType, double)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<float(sd::LongType, float)>& func,
NDArray* target);
#endif
#if defined(HAS_FLOAT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<float16(sd::LongType, float16)>& func,
NDArray* target);
#endif
#if defined(HAS_BFLOAT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<bfloat16(sd::LongType, bfloat16)>& func,
NDArray* target);
#endif
#if defined(HAS_INT64)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(
std::function<sd::LongType(sd::LongType, sd::LongType)>& func, NDArray* target);
#endif
#if defined(HAS_INT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<int(sd::LongType, int)>& func,
NDArray* target);
#endif
#if defined(HAS_INT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<int16_t(sd::LongType, int16_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT8)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<uint8_t(sd::LongType, uint8_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<uint16_t(sd::LongType, uint16_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UINT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<uint32_t(sd::LongType, uint32_t)>& func,
NDArray* target);
#endif
#if defined(HAS_UNSIGNEDLONG)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<uint64_t(sd::LongType, uint64_t)>& func,
NDArray* target);
#endif
#if defined(HAS_INT8)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<int8_t(sd::LongType, int8_t)>& func,
NDArray* target);
#endif
#if defined(HAS_BOOL)
template SD_LIB_HIDDEN void NDArray::applyIndexedLambda(std::function<bool(sd::LongType, bool)>& func,
NDArray* target);
#endif
//////////////////////////////////////////////////////////////////////////
template <typename T>
SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(NDArray* other, std::function<T(sd::LongType, T, T)>& func,
NDArray* target) {
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyIndexedPairwiseLambda<T> method: wrong template parameter T, its type should be the same as "
"type of this array!");
if (dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyIndexedPairwiseLambda<T> method: types of this and target array should match !");
if (this->lengthOf() != other->lengthOf()) {
sd_printf("applyIndexedPairwiseLambda requires both operands to have the same shape\n", "");
THROW_EXCEPTION("Shapes mismatch");
}
auto f = this->bufferAsT<T>();
auto s = other->bufferAsT<T>();
auto z = target->bufferAsT<T>();
if (f == z) {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto yOffset = other->getOffset(e);
f[xOffset] = func((sd::LongType)e, f[xOffset], s[yOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
} else {
auto loop = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
auto xOffset = this->getOffset(e);
auto yOffset = other->getOffset(e);
auto zOffset = target->getOffset(e);
z[zOffset] = func((sd::LongType)e, f[xOffset], s[yOffset]);
}
};
samediff::Threads::parallel_for(loop, 0, _length);
}
}
#if defined(HAS_DOUBLE)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<double(sd::LongType, double, double)>& func, NDArray* target);
#endif
#if defined(HAS_FLOAT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<float(sd::LongType, float, float)>& func, NDArray* target);
#endif
#if defined(HAS_FLOAT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<float16(sd::LongType, float16, float16)>& func, NDArray* target);
#endif
#if defined(HAS_BFLOAT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<bfloat16(sd::LongType, bfloat16, bfloat16)>& func, NDArray* target);
#endif
#if defined(HAS_INT64)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<sd::LongType(sd::LongType, sd::LongType, sd::LongType)>& func, NDArray* target);
#endif
#if defined(HAS_INT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(NDArray* other,
std::function<int(sd::LongType, int, int)>& func,
NDArray* target);
#endif
#if defined(HAS_INT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<int16_t(sd::LongType, int16_t, int16_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT8)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<uint8_t(sd::LongType, uint8_t, uint8_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT16)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<uint16_t(sd::LongType, uint16_t, uint16_t)>& func, NDArray* target);
#endif
#if defined(HAS_UINT32)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<uint32_t(sd::LongType, uint32_t, uint32_t)>& func, NDArray* target);
#endif
#if defined(HAS_UNSIGNEDLONG)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<uint64_t(sd::LongType, uint64_t, uint64_t)>& func, NDArray* target);
#endif
#if defined(HAS_INT8)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<int8_t(sd::LongType, int8_t, int8_t)>& func, NDArray* target);
#endif
#if defined(HAS_BOOL)
template SD_LIB_HIDDEN void NDArray::applyIndexedPairwiseLambda(
NDArray* other, std::function<bool(sd::LongType, bool, bool)>& func, NDArray* target);
#endif
@@ -0,0 +1,47 @@
/*
* ******************************************************************************
* *
* *
* * 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/CudaPointerDeallocator.h>
namespace sd {
void CudaPointerDeallocator::release(void *ptr) {
if (ptr == nullptr) return;
// Check if this is a valid device pointer before freeing
cudaPointerAttributes attributes;
cudaError_t result = cudaPointerGetAttributes(&attributes, ptr);
if (result == cudaSuccess) {
// Only free if it's a regular device pointer
// cudaMemoryTypeDevice is for regular allocations we can free
if (attributes.type == cudaMemoryTypeDevice) {
cudaFree(ptr);
}
// Don't free other types (like constant memory)
} else {
// Clear the error and don't try to free this pointer
cudaGetLastError(); // Clear the error state
}
}
} // namespace sd
+608
View File
@@ -0,0 +1,608 @@
/* ******************************************************************************
*
*
* 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/DataTypeUtils.h>
#include <exceptions/allocation_exception.h>
#include <exceptions/cuda_exception.h>
#include <execution/AffinityManager.h>
#include <memory/MemoryCounter.h>
#include <system/op_boilerplate.h>
#include <system/type_boilerplate.h>
#include "../DataBuffer.h"
#include "helpers/DebugHelper.h"
#if defined(SD_GCC_FUNCTRACE)
#include <array/DataBufferLifecycleTracker.h>
#endif
namespace sd {
void DataBuffer::expand(const uint64_t size) {
if (size > _lenInBytes) {
// allocate new buffer
int8_t* newBuffer = nullptr;
int8_t* newSpecialBuffer = nullptr;
ALLOCATE_SPECIAL(newSpecialBuffer, _workspace, size, int8_t);
// copy data from existing buffer
if (_primaryBuffer != nullptr) {
// there's non-zero chance that primary buffer doesn't exist yet
ALLOCATE(newBuffer, _workspace, size, int8_t);
std::memcpy(newBuffer, _primaryBuffer, _lenInBytes);
if (_isOwnerPrimary) {
auto ipb = reinterpret_cast<int8_t*>(_primaryBuffer);
RELEASE(ipb, _workspace);
}
_primaryBuffer = newBuffer;
_isOwnerPrimary = true;
}
cudaMemcpy(newSpecialBuffer, _specialBuffer, _lenInBytes, cudaMemcpyDeviceToDevice);
if (_isOwnerSpecial) {
auto isb = reinterpret_cast<int8_t*>(_specialBuffer);
RELEASE_SPECIAL(isb, _workspace);
}
_specialBuffer = newSpecialBuffer;
_lenInBytes = size;
_isOwnerSpecial = true;
}
}
DataBuffer DataBuffer::dup() {
DataBuffer result;
result._dataType = _dataType;
result._lenInBytes = _lenInBytes;
result._primaryBuffer = _primaryBuffer;
result._specialBuffer = _specialBuffer;
result._isOwnerPrimary = _isOwnerPrimary;
result._isOwnerSpecial = _isOwnerSpecial;
result.allocateBuffers(true);
result.copyCounters(*this);
result.copyBufferFrom(*this);
return result;
}
template <typename T>
void* DataBuffer::primaryAtOffset(const LongType offset) {
if(_primaryBuffer == nullptr)
return nullptr;
T *type = reinterpret_cast<T*>(_primaryBuffer);
return reinterpret_cast<void *>(type + offset);
}
template <typename T>
void* DataBuffer::specialAtOffset(const LongType offset) {
if(_specialBuffer == nullptr)
return nullptr;
T *type = reinterpret_cast<T*>(_specialBuffer);
return reinterpret_cast<void *>(type + offset);
}
#define PRIMARYOFFSET(T) template SD_LIB_EXPORT void* DataBuffer::primaryAtOffset<GET_SECOND(T)>(sd::LongType offset);
ITERATE_LIST((SD_COMMON_TYPES),PRIMARYOFFSET)
#define SPECIALOFFSET(T) template SD_LIB_EXPORT void* DataBuffer::specialAtOffset<GET_SECOND(T)>(sd::LongType offset);
ITERATE_LIST((SD_COMMON_TYPES),SPECIALOFFSET)
template <typename T>
void _printHostBuffer(DataBuffer* buffer, long offset) {
sd::LongType len = buffer->getNumElements();
auto buff = buffer->template primaryAsT<T>();
sd::LongType limit = len;
if (limit == -1 || limit >= buffer->getNumElements()) {
limit = buffer->getNumElements();
}
const char* msg = nullptr;
if (msg != nullptr) {
printf("%s: ", msg);
} else {
printf("[");
}
sd::DataType dataType = buffer->getDataType();
auto baseOffset = offset;
if (dataType == sd::DataType::DOUBLE || dataType == sd::DataType::FLOAT32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (e > offset) printf(", ");
if (dataType == sd::DataType::DOUBLE) {
printf("%.15f", buff[e]);
} else {
printf("%.15f", static_cast<float>(buff[e]));
}
}
} else if (dataType == sd::DataType::INT64 || dataType == sd::DataType::UINT64 ||
dataType == sd::DataType::INT32 || dataType == sd::DataType::UINT32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (dataType == sd::DataType::INT64 || dataType == sd::DataType::UINT64) {
printf("%lld", static_cast<long long>(buff[e]));
} else {
printf("%d", static_cast<int>(buff[e]));
}
if (e < limit - 1) {
printf(", ");
}
}
} else if (dataType == sd::DataType::BOOL) {
for (sd::LongType e = baseOffset; e < limit; e++) {
if (static_cast<bool>(buff[e])) {
printf("true");
} else {
printf("false");
}
if (e < limit - 1) {
printf(", ");
}
}
} else if (dataType == sd::DataType::UTF8 || dataType == sd::DataType::UTF16 ||
dataType == sd::DataType::UTF32) {
for (sd::LongType e = baseOffset; e < limit; e++) {
printf("\"%s\"", reinterpret_cast<const char*>(&buff[e]));
if (e < limit - 1) {
printf(", ");
}
}
}
printf("]\n");
fflush(stdout);
}
void DataBuffer::printHostDevice(long offset) {
THROW_EXCEPTION("");
}
void DataBuffer::printSpecialAllocationTraces() {
//no op on purpose
}
void DataBuffer::showBufferLimited() {
}
template <typename T>
SD_KERNEL void printDeviceBufferKernel(void* buffer, sd::LongType offset, sd::LongType length) {
T* typedBuffer = reinterpret_cast<T*>(buffer);
if (threadIdx.x == 0 && blockIdx.x == 0) {
printf("[ ");
for (sd::LongType i = offset; i < offset + length; i++) {
// Cast to double for consistent formatting
printf("%g ", (double)typedBuffer[i]);
}
printf("]");
}
}
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT SD_KERNEL void printDeviceBufferKernel,(void* buffer, sd::LongType offset, sd::LongType length),SD_COMMON_TYPES);
// Wrapper function to launch the kernel
template <typename T>
void launchPrintDeviceBufferKernel(void* buffer, sd::LongType offset, sd::LongType length) {
printDeviceBufferKernel<T><<<1, 1, 32*1024, *LaunchContext::defaultContext()->getCudaStream()>>>(
buffer, offset, length);
cudaStreamSynchronize(*LaunchContext::defaultContext()->getCudaStream());
sd::DebugHelper::checkErrorCode(LaunchContext::defaultContext()->getCudaStream(),
"printBufferDebug kernel failed");
}
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void launchPrintDeviceBufferKernel,(void* buffer, sd::LongType offset, sd::LongType length),SD_COMMON_TYPES);
template <typename T>
void DataBuffer::printHostBufferContent(void* buffer, sd::LongType offset, sd::LongType length) {
T* typedBuffer = reinterpret_cast<T*>(buffer);
sd_printf("[ ", 0);
for (sd::LongType i = offset; i < offset + length; i++) {
// For numeric types, cast to double for consistent formatting
if (std::is_arithmetic<T>::value) {
sd_printf("%g ", (double)typedBuffer[i]);
} else {
// For non-numeric types, print as hex
sd_printf("0x%x ", *reinterpret_cast<int*>(&typedBuffer[i]));
}
}
sd_printf("]", 0);
}
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void DataBuffer::printHostBufferContent,(void* buffer, sd::LongType offset, sd::LongType length),SD_COMMON_TYPES);
// DataBuffer implementation for .cu file
void DataBuffer::printBufferDebug(const char* msg, sd::LongType offset, sd::LongType limit) {
if (msg) sd_printf("%s:\n", msg);
// Print metadata
sd_printf("DataBuffer: DataType=%s, Length=%lld elements, DeviceId=%d\n",
DataTypeUtils::asString(_dataType).c_str(), (long long)getNumElements(), deviceId());
// Print host buffer content
if (_primaryBuffer != nullptr) {
sd_printf("Host buffer (@%p): ", _primaryBuffer);
sd::LongType len = getNumElements();
sd::LongType printLen = limit < 0 ? len : std::min(len - offset, limit);
// Print based on datatype
BUILD_SINGLE_SELECTOR(_dataType, printHostBufferContent,
(_primaryBuffer, offset, printLen), SD_COMMON_TYPES);
if (offset + printLen < len) sd_printf("... ", 0);
sd_printf("\n", 0);
} else {
sd_printf("Host buffer: nullptr\n", 0);
}
// Print device buffer using kernel
if (_specialBuffer != nullptr) {
sd_printf("Device buffer (@%p): ", _specialBuffer);
sd::LongType len = getNumElements();
sd::LongType printLen = limit < 0 ? len : std::min(len - offset, limit);
// Launch kernel through wrapper function
BUILD_SINGLE_SELECTOR(_dataType, launchPrintDeviceBufferKernel,
(_specialBuffer, offset, printLen), SD_COMMON_TYPES);
sd_printf("\n", 0);
} else {
sd_printf("Device buffer: nullptr\n", 0);
}
// Print sync state counters
sd_printf("Sync state: _counter=%lld, _writePrimary=%lld, _writeSpecial=%lld, _readPrimary=%lld, _readSpecial=%lld\n",
(long long)_counter.load(), (long long)_writePrimary.load(), (long long)_writeSpecial.load(),
(long long)_readPrimary.load(), (long long)_readSpecial.load());
sd_printf("isPrimaryActual=%d, isSpecialActual=%d\n", isPrimaryActual(), isSpecialActual());
}
void DataBuffer::showCounters(const char* msg1, const char* msg2) {
sd_debug("%s %s || primary %p special %p :: wP: %d wS: %d rP: %d rS: %d\n", msg1, msg2, _primaryBuffer,
_specialBuffer, (int)_writePrimary.load(), (int)_writeSpecial.load(), (int)_readPrimary.load(),
(int)_readSpecial.load());
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::allocateSpecial() {
if (_specialBuffer != nullptr) {
return;
}
if (_lenInBytes == 0) {
std::string errorMessage;
errorMessage += "DataBuffer::allocateSpecial: ";
errorMessage += "Special buffer is already allocated";
errorMessage += " or length is 0";
errorMessage += "Length is: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += "Special buffer is nullptr : ";
errorMessage += std::to_string(_specialBuffer == nullptr);
THROW_EXCEPTION(errorMessage.c_str());
}
#if defined(SD_GCC_FUNCTRACE)
if(Environment::getInstance().isFuncTracePrintAllocate()) {
allocationStackTraceSpecial = new StackTrace();
allocationStackTraceSpecial->load_here();
}
#endif
if (_specialBuffer == nullptr) {
auto deviceId = AffinityManager::currentDeviceId();
if (_workspace == nullptr) {
if (!memory::MemoryCounter::getInstance().validate(getLenInBytes())) {
std::string errorMessage;
errorMessage += "DataBuffer::allocateSpecial: ";
errorMessage += "Requested amount exceeds device limits";
errorMessage += "DeviceId: ";
errorMessage += std::to_string(deviceId);
errorMessage += "Device limit: ";
errorMessage += std::to_string(memory::MemoryCounter::getInstance().deviceLimit(deviceId));
errorMessage += "Requested amount: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += "Special buffer is nullptr : ";
errorMessage += std::to_string(_specialBuffer == nullptr);
THROW_EXCEPTION(errorMessage.c_str());
}
}
ALLOCATE_SPECIAL(_specialBuffer, _workspace, getLenInBytes(), int8_t);
_isOwnerSpecial = true;
#if defined(SD_GCC_FUNCTRACE)
// Record SPECIAL (device) buffer allocation
array::DataBufferLifecycleTracker::getInstance().recordAllocation(
_specialBuffer, getLenInBytes(), getDataType(),
array::BufferType::SPECIAL, this, _workspace != nullptr);
#endif
if (_workspace == nullptr) {
memory::MemoryCounter::getInstance().countIn(deviceId, getLenInBytes());
memory::MemoryCounter::getInstance().countIn(memory::MemoryType::DEVICE, getLenInBytes());
}
} else if(getLenInBytes() == 0) {
std::string errorMessage;
errorMessage += "DataBuffer::allocateSpecial: ";
errorMessage += "Special buffer is already allocated";
errorMessage += " or length is 0";
errorMessage += "Length is: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += "Special buffer is nullptr : ";
errorMessage += std::to_string(_specialBuffer == nullptr);
THROW_EXCEPTION(errorMessage.c_str());
}
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::syncToPrimary(const LaunchContext* context, const bool forceSync) {
if (isPrimaryActual() && !forceSync) {
return;
}
allocatePrimary();
auto res = cudaStreamSynchronize(*context->getCudaStream());
if (res != 0) {
std::string errorMessage;
errorMessage += "DataBuffer::syncToPrimary: cudaStreamSynchronize failed: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += cudaGetErrorString(res);
errorMessage += "Special buffer is nullptr : ";
THROW_EXCEPTION(errorMessage.c_str());
}
res = cudaMemcpy(_primaryBuffer, _specialBuffer, getLenInBytes(), cudaMemcpyDeviceToHost);
if (res != 0) {
std::string errorMessage;
errorMessage += "DataBuffer::syncToPrimary: cudaMemcpy failed: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += cudaGetErrorString(res);
errorMessage += "Special buffer is nullptr : ";
errorMessage += std::to_string(_specialBuffer == nullptr);
THROW_EXCEPTION(errorMessage.c_str());
}
readPrimary();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::syncToSpecial(const bool forceSync) {
// in this case there's nothing to do here
if (_primaryBuffer == nullptr) return;
if (isSpecialActual() && !forceSync) {
return;
}
allocateSpecial();
auto res = cudaMemcpy(_specialBuffer, _primaryBuffer, getLenInBytes(), cudaMemcpyHostToDevice);
if (res != 0) {
std::string errorMessage;
errorMessage += "Failed to copy dataBuffer::syncToSpecial: ";
errorMessage += std::to_string(getLenInBytes());
errorMessage += cudaGetErrorString(res);
THROW_EXCEPTION(errorMessage.c_str());
}
readSpecial();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::deleteSpecial() {
if (_isOwnerSpecial && _specialBuffer != nullptr && getLenInBytes() != 0) {
auto p = reinterpret_cast<int8_t*>(_specialBuffer);
#if defined(SD_GCC_FUNCTRACE)
// Record SPECIAL (device) buffer deallocation before releasing
array::DataBufferLifecycleTracker::getInstance().recordDeallocation(
_specialBuffer, array::BufferType::SPECIAL);
#endif
RELEASE_SPECIAL(p, _workspace);
_specialBuffer = nullptr;
_isOwnerSpecial = false;
// count out towards DataBuffer device, only if we're not in workspace
if (_workspace == nullptr) {
sd::memory::MemoryCounter::getInstance().countOut(_deviceId, getLenInBytes());
sd::memory::MemoryCounter::getInstance().countOut(sd::memory::MemoryType::DEVICE, getLenInBytes());
}
}
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setCountersToZero() {
_counter.store(0L);
_writePrimary.store(0L);
_writeSpecial.store(0L);
_readPrimary.store(0L);
_readSpecial.store(0L);
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyCounters(const DataBuffer& other) {
_counter.store(other._counter);
_writePrimary.store(other._readSpecial);
_writeSpecial.store(other._readPrimary);
_readPrimary.store(other._writeSpecial);
_readSpecial.store(other._writePrimary);
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyBufferFrom(const DataBuffer& other, size_t sizeToCopyinBytes, const sd::LongType offsetThis,
const sd::LongType offsetOther) { // copies only to special buffer
if (other._primaryBuffer == nullptr && other._specialBuffer == nullptr) {
return;
}
if (sizeToCopyinBytes == 0) {
sizeToCopyinBytes = other.getLenInBytes();
}
if (sizeToCopyinBytes == 0) {
return;
}
if (other.isPrimaryActual()) {
auto res = cudaMemcpy(
static_cast<int8_t*>(_specialBuffer) + offsetThis * DataTypeUtils::sizeOfElement(_dataType),
static_cast<const int8_t*>(other._primaryBuffer) + offsetOther * DataTypeUtils::sizeOfElement(other._dataType),
sizeToCopyinBytes, cudaMemcpyHostToDevice);
if (res != 0)
throw cuda_exception::build("DataBuffer::copyBufferFrom: cudaMemcpy_cudaMemcpyHostToDevice failed!", res);
other.readPrimary();
} else {
auto res = cudaMemcpy(
static_cast<int8_t*>(_specialBuffer) + offsetThis * DataTypeUtils::sizeOfElement(_dataType),
static_cast<const int8_t*>(other._specialBuffer) + offsetOther * DataTypeUtils::sizeOfElement(other._dataType),
sizeToCopyinBytes, cudaMemcpyDeviceToDevice);
if (res != 0)
throw cuda_exception::build("DataBuffer::copyBufferFrom: cudaMemcpy_cudaMemcpyDeviceToDevice failed!", res);
other.readSpecial();
}
writeSpecial();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::copyBufferFromHost(const void* hostBuffer, size_t sizeToCopyinBytes, const sd::LongType offsetThis,
const sd::LongType offsetHostBuffer) { // copies only to special buffer
if (hostBuffer == nullptr) return;
if (sizeToCopyinBytes == 0) sizeToCopyinBytes = getLenInBytes();
if (sizeToCopyinBytes == 0) return;
auto res =
cudaMemcpy(static_cast<int8_t*>(_specialBuffer) + offsetThis * DataTypeUtils::sizeOfElement(_dataType),
static_cast<const int8_t*>(hostBuffer) + offsetHostBuffer * DataTypeUtils::sizeOfElement(_dataType),
sizeToCopyinBytes, cudaMemcpyHostToDevice);
if (res != 0)
throw cuda_exception::build("DataBuffer::copyBufferFromHost: cudaMemcpy_cudaMemcpyHostToDevice failed!", res);
writeSpecial();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setSpecial(void* special, const bool isOwnerSpecial) {
deleteSpecial();
_specialBuffer = special;
_isOwnerSpecial = isOwnerSpecial;
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::allocateBuffers(const bool allocBoth) { // always allocate special buffer only (cuda case)
allocateSpecial();
if (allocBoth) allocatePrimary();
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::setToZeroBuffers(const bool both) {
if(getLenInBytes() < 1 || special() == nullptr)
return;
cudaMemsetAsync(special(), 0, getLenInBytes(), *LaunchContext::defaultContext()->getCudaStream());
auto res = cudaStreamSynchronize(*LaunchContext::defaultContext()->getCudaStream());
if (res != 0) throw cuda_exception::build("DataBuffer::setToZeroBuffers: streamSync failed!", res);
writeSpecial();
if (both) {
memset(primary(), 0, getLenInBytes());
readPrimary();
}
}
/////////////////////////
template <typename T>
void memcpyWithT(DataBuffer* dst, DataBuffer* src, sd::LongType startingOffset, sd::LongType dstOffset) {
if (src->getLenInBytes() > dst->getLenInBytes())
THROW_EXCEPTION("DataBuffer::memcpy: Source data buffer is larger than destination");
int res = 0;
if (src->isSpecialActual()) {
res = cudaMemcpyAsync(dst->specialAtOffset<T>(dstOffset), src->specialAtOffset<T>(startingOffset), src->getLenInBytes(), cudaMemcpyDeviceToDevice,
*LaunchContext::defaultContext()->getCudaStream());
} else if (src->isPrimaryActual()) {
res = cudaMemcpyAsync(dst->specialAtOffset<T>(dstOffset), src->specialAtOffset<T>(startingOffset), src->getLenInBytes(), cudaMemcpyHostToDevice,
*LaunchContext::defaultContext()->getCudaStream());
}
if (res != 0) throw cuda_exception::build("DataBuffer::memcpy: cudaMemcpyAsync failed!", res);
res = cudaStreamSynchronize(*LaunchContext::defaultContext()->getCudaStream());
if (res != 0) throw cuda_exception::build("DataBuffer::memcpy: streamSync failed!", res);
dst->writeSpecial();
}
void DataBuffer::memcpy(DataBuffer* dst, DataBuffer* src,
sd::LongType startingOffset, sd::LongType dstOffset) {
BUILD_SINGLE_TEMPLATE(memcpyWithT,(dst, src, startingOffset, dstOffset),
SD_COMMON_TYPES);
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::migrate() {
memory::Workspace* newWorkspace = nullptr;
void* newBuffer;
ALLOCATE_SPECIAL(newBuffer, newWorkspace, getLenInBytes(), int8_t);
auto res = cudaMemcpy(newBuffer, _specialBuffer, getLenInBytes(), cudaMemcpyDeviceToDevice);
if (res != 0) throw cuda_exception::build("DataBuffer::migrate: cudaMemcpyAsync failed!", res);
if (_isOwnerSpecial) {
// now we're releasing original buffer
RELEASE_SPECIAL(_specialBuffer, _workspace);
}
_isOwnerSpecial = true;
_specialBuffer = newBuffer;
}
////////////////////////////////////////////////////////////////////////
void DataBuffer::writePrimary() const { _writePrimary = ++_counter; }
void DataBuffer::writeSpecial() const { _writeSpecial = ++_counter; }
void DataBuffer::readPrimary() const { _readPrimary = ++_counter; }
void DataBuffer::readSpecial() const { _readSpecial = ++_counter; }
bool DataBuffer::isPrimaryActual() const {
return (_writePrimary.load() > _writeSpecial.load() || _readPrimary.load() > _writeSpecial.load());
}
bool DataBuffer::isSpecialActual() const {
return (_writeSpecial.load() > _writePrimary.load() || _readSpecial.load() > _writePrimary.load());
}
} // namespace sd
+720
View File
@@ -0,0 +1,720 @@
/* ******************************************************************************
*
*
* 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
******************************************************************************/
#ifndef NDARRAY_CPP
#define NDARRAY_CPP
#include <array/NDArray.h>
#include <array/NDArrayFactory.h>
#include <exceptions/cuda_exception.h>
#include <exceptions/datatype_exception.h>
#include <helpers/ArrayUtils.h>
#include <helpers/ConstantShapeHelper.h>
#include <helpers/MmulHelper.h>
#include <helpers/PointersManager.h>
#include <helpers/ShapeUtils.h>
#include <helpers/logger.h>
#include <helpers/threshold.h>
#include <indexing/IndicesList.h>
#include <indexing/NDIndex.h>
#include <legacy/NativeOpExecutioner.h>
#include <loops/broadcasting.h>
#include <loops/pairwise_transform.h>
#include <loops/random.h>
#include <loops/special_kernels.h>
#include <loops/transform_same.h>
#include <memory/MemoryRegistrator.h>
#include <memory/Workspace.h>
#include <ops/ops.h>
#include <ops/specials_cuda.h>
#include <array/NDArray.hXX>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <system/selective_rendering.h>
#include "execution/cuda/LaunchDims.h"
namespace sd {
void* NDArray::platformBuffer() { return specialBuffer(); }
void NDArray::syncToDevice() {
auto currentDeviceId = AffinityManager::currentDeviceId();
if (currentDeviceId != _deviceId) {
// first of all we update shapeInfo
const_cast<NDArray*>(this)->setShapeInfo(this->shapeInfo());
// now we actually migrate data buffer
_buffer->migrate();
}
_buffer->syncToSpecial();
}
void NDArray::syncToHost() { if(!isEmpty()) _buffer->syncToPrimary(getContext()); }
void NDArray::tickWriteHost() { if(!isEmpty()) _buffer->writePrimary(); }
void NDArray::tickWriteDevice() { if(!isEmpty()) _buffer->writeSpecial(); }
void NDArray::tickReadHost() { if(!isEmpty()) _buffer->readPrimary(); }
void NDArray::tickReadDevice() { if(!isEmpty()) _buffer->readSpecial(); }
void NDArray::tickBothActual() {
_buffer->writePrimary();
_buffer->readSpecial();
}
bool NDArray::isActualOnHostSide() { return _buffer->isPrimaryActual(); }
bool NDArray::isActualOnDeviceSide() { return _buffer->isSpecialActual(); }
void NDArray::makeBothBuffersActual() {
if (!isActualOnHostSide()) syncToHost();
if (!isActualOnDeviceSide()) syncToDevice();
}
///////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void fillAsTriangularCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const T val, const int lower,
const int upper, char direction, bool includeEdges) {
const auto x = reinterpret_cast<const T*>(vx);
auto z = reinterpret_cast<T*>(vz);
__shared__ LongType zRank, xRank, areSameOffsets, *sharedMem; // xRank == zRank always, except when xRank = 1, in this case zRank = 2
__shared__ LongType zLen, totalThreads; // xLen == zLen, except when xRank = 1, in this case zLen = 2*xLen
__shared__ LongType *zShape;
__shared__ LongType *zStride;
__shared__ LongType *xShape;
__shared__ LongType *xStride;
if (threadIdx.x == 0) {
extern __shared__ unsigned char shmem[];
sharedMem = reinterpret_cast<LongType*>(shmem);
areSameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
xRank = shape::rank(xShapeInfo);
zRank = shape::rank(zShapeInfo);
zLen = shape::length(zShapeInfo);
totalThreads = gridDim.x * blockDim.x;
zShape = shape::shapeOf(zShapeInfo);
zStride = shape::stride(zShapeInfo);
xShape = shape::shapeOf(xShapeInfo);
xStride = shape::stride(xShapeInfo);
}
__syncthreads();
auto coords = sharedMem + threadIdx.x * zRank;
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
bool dirU = direction == 'u';
bool dirL = direction == 'l';
for (LongType i = tid; i < zLen; i += totalThreads) {
INDEX2COORDS(i, zRank, zShape, coords);
LongType zOffset;
COORDS2INDEX(zRank, zStride, coords, zOffset);
auto row = coords[zRank - 2];
auto col = coords[zRank - 1];
auto lCompare = includeEdges ? row + lower <= col : row + lower < col;
auto uCompare = includeEdges ? row + upper >= col : row + upper > col;
if (dirU && lCompare || dirL && uCompare) {
z[zOffset] = val;
} else if (vx != vz) { // when x and z are different arrays
if (xRank != zRank) coords[0] = coords[1];
LongType xOffset;
COORDS2INDEX(xRank, xStride, coords, xOffset);
z[zOffset] = x[xOffset];
}
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
void NDArray::fillAsTriangular(const float val, int lower, int upper, NDArray& target, const char direction,
const bool includeEdges) {
if (isS()) THROW_EXCEPTION("NDArray::fillAsTriangular: you can't use this method on String array!");
if (!isSameShape(target) &&
!(rankOf() == 1 && target.rankOf() == 2 && sizeAt(0) == target.sizeAt(0) && sizeAt(0) == target.sizeAt(1)))
throw std::string("NDArray::fillAsTriangular method: wrong shape of target array !");
const int threadsPerBlock = SD_MAX_NUM_THREADS / 4;
int len = target.isScalar() ? 1 : target.lengthOf();
const int blocksPerGrid = (len + threadsPerBlock - 1) / threadsPerBlock;
const int sharedMem = threadsPerBlock * sizeof(int) * target.rankOf() + 128;
dim3 launchDims = getFillTriLaunchDims(target.lengthOf(), target.rankOf());
PointersManager manager(getContext(), "NDArray::fillAsTriangular");
prepareSpecialUse({&target}, {this});
fillAsTriangularCuda<T><<<launchDims.y, launchDims.x, launchDims.z, *getContext()->getCudaStream()>>>(
platformBuffer(), specialShapeInfo(), target.platformBuffer(), target.specialShapeInfo(), static_cast<T>(val),
lower, upper, direction, includeEdges);
registerSpecialUse({&target}, {this});
sd::DebugHelper::checkGlobalErrorCode("fillTriangular failed");
manager.synchronize();
}
BUILD_SINGLE_TEMPLATE( SD_LIB_EXPORT void NDArray::fillAsTriangular,
(const float val, int lower, int upper, NDArray& target, const char direction,
const bool includeEdges),
SD_COMMON_TYPES);
////////////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void identityMatrixCuda(void* vx, const LongType* xShapeInfo, const T val) {
auto x = reinterpret_cast<T*>(vx);
// Shared memory variables
__shared__ LongType rank;
__shared__ LongType len;
__shared__ LongType totalThreads;
__shared__ const LongType* shapePtr;
__shared__ const LongType* stridePtr;
__shared__ LongType* sharedMem;
// Initialize shared variables in thread 0
if (threadIdx.x == 0) {
extern __shared__ unsigned char shmem[];
sharedMem = reinterpret_cast<LongType*>(shmem);
// Cache rank and length
rank = shape::rank(xShapeInfo);
len = shape::length(xShapeInfo);
// Cache pointers to shape and stride arrays
shapePtr = shape::shapeOf(xShapeInfo);
stridePtr = shape::stride(xShapeInfo);
// Calculate total number of threads
totalThreads = gridDim.x * blockDim.x;
}
__syncthreads();
// Each thread has its own coordinates array in shared memory
auto coords = sharedMem + threadIdx.x * rank;
// Calculate global thread ID
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
// Iterate over assigned elements
for (LongType i = tid; i < len; i += totalThreads) {
// Convert linear index to multi-dimensional coordinates using cached shape
INDEX2COORDS(i, rank, shapePtr, coords);
// Compute linear offset from coordinates using cached stride
LongType offset;
COORDS2INDEX(rank, stridePtr, coords, offset);
// Check if the current position is on the diagonal (row == col)
if (coords[rank - 2] == coords[rank - 1]) { // Assuming 0-based indexing
x[offset] = val;
}
else {
x[offset] = static_cast<T>(0);
}
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
static void identityMatrixCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, void* vx, const LongType* xShapeInfo,
const float val) {
identityMatrixCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, static_cast<T>(val));
sd::DebugHelper::checkGlobalErrorCode("identityMatrix failed");
}
BUILD_SINGLE_TEMPLATE( void identityMatrixCudaLauncher,
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, void* vx, const sd::LongType* xShapeInfo, const float val),
SD_COMMON_TYPES);
////////////////////////////////////////////////////////////////////////
void NDArray::setIdentity() {
if (isS()) THROW_EXCEPTION("NDArray::setIdentity: you can't use this method on String array!");
int len = isScalar() ? 1 : lengthOf();
dim3 launchDims = getIdentityLaunchDims(len, rankOf());
PointersManager manager(getContext(), "NDArray::setIdentity");
syncToDevice();
BUILD_SINGLE_SELECTOR(dataType(), identityMatrixCudaLauncher,
(launchDims.y, launchDims.x,launchDims.z, getContext()->getCudaStream(), platformBuffer(),
specialShapeInfo(), 1.f),
SD_COMMON_TYPES);
tickWriteDevice();
manager.synchronize();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void NDArray::swapUnsafe(NDArray& other) {
auto xType = this->dataType();
if (xType != other.dataType())
THROW_EXCEPTION("NDArray::swapUnsage method: both arrays must have the same data type");
if (specialBuffer() == nullptr || other.specialBuffer() == nullptr)
THROW_EXCEPTION("NDArray::swapUnsafe method: input array should not be empty!");
if (lengthOf() != other.lengthOf())
THROW_EXCEPTION("NDArray::swapUnsafe method: input arrays should have the same length!");
PointersManager manager(getContext(), "NDArray::swapUnsafe");
prepareSpecialUse({&other, this}, {&other, this});
BUILD_SINGLE_SELECTOR(xType, templatedSwapUnsafe,
(specialBuffer(), specialShapeInfo(), other.specialBuffer(), other.specialShapeInfo(),
getContext()->getCudaStream()),
SD_COMMON_TYPES);
registerSpecialUse({&other, this}, {&other, this});
manager.synchronize();
}
////////////////////////////////////////////////////////////////////////
void NDArray::synchronize(const char* msg) {
auto res = cudaStreamSynchronize(*(getContext()->getCudaStream()));
if (res != 0) {
std::string message = msg + std::string(": synchronization failed !");
THROW_EXCEPTION(message.c_str());
}
}
// NDArray implementation for .cu file
void NDArray::printBufferDebug(const char* msg, sd::LongType offset, sd::LongType limit) {
if (msg) sd_printf("%s:\n", msg);
if(limit < 0) limit = lengthOf();
// Print array info
sd_printf("NDArray: Shape=[", 0);
for (int i = 0; i < rankOf(); i++) {
sd_printf("%lld", (long long)sizeAt(i));
if (i < rankOf() - 1) sd_printf(",", 0);
}
sd_printf("], DataType=%s, Order=%c\n",
DataTypeUtils::asString(dataType()).c_str(), ordering());
#if defined(SD_GCC_FUNCTRACE)
printf("========================================================\n");
Printer p;
StackTrace st;
st.load_here();
p.print(st);
printf("========================================================\n");
fflush(stdout);
#endif
// Print buffer state
if (_buffer != nullptr) {
_buffer->printBufferDebug("Buffer contents", offset, limit);
} else {
sd_printf("Buffer is nullptr\n", 0);
}
}
////////////////////////////////////////////////////////////////////////
void NDArray::prepareSpecialUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList, bool synchronizeWritables) {
for (const auto& a : readList)
if (a != nullptr) a->syncToDevice();
for (const auto& a : writeList) {
if (a != nullptr) {
a->getDataBuffer()->allocateSpecial();
if (synchronizeWritables) a->syncToDevice();
}
}
}
////////////////////////////////////////////////////////////////////////
void NDArray::registerSpecialUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList) {
for (const auto& p : readList)
if (p != nullptr) p->tickReadDevice();
for (const auto& p : writeList)
if (p != nullptr) p->tickWriteDevice();
}
////////////////////////////////////////////////////////////////////////
void NDArray::preparePrimaryUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList, bool synchronizeWritables) {
for (const auto& a : readList)
if (a != nullptr) a->syncToHost();
for (const auto& a : writeList) {
if (a != nullptr) {
a->getDataBuffer()->allocatePrimary();
if (synchronizeWritables) a->syncToHost();
}
}
}
////////////////////////////////////////////////////////////////////////
void NDArray::registerPrimaryUse(const std::vector<NDArray*>& writeList,
const std::vector<NDArray*>& readList) {
for (const auto& p : readList)
if (p != nullptr) p->tickReadHost();
for (const auto& p : writeList)
if (p != nullptr) p->tickWriteHost();
}
//////////////////////////////////////////////////////////////////////////
void NDArray::syncShape() {
cudaMemcpy(const_cast<LongType*>(specialShapeInfo()), shapeInfo(), shape::shapeInfoByteLength(shapeInfo()),
cudaMemcpyHostToDevice);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// change an array by repeating it the number of times given by reps.
NDArray NDArray::tile(const std::vector<LongType>& reps) {
int dim = reps.size();
LongType product = 1;
for (const auto& item : reps) product *= item;
if (product < 1) THROW_EXCEPTION("NDArray::tile method: one of the elements in reps array is zero !");
int rankOld = rankOf();
int diff = rankOld - dim;
if (product == 1) { // in this case 2 possibilities are present: just reshape or nothing to do
NDArray result(*this);
if (diff < 0) { // reshape to higher dimension
std::vector<LongType> shapeNew = reps; // need to have unities at first "diff" positions of new shape
memcpy(&shapeNew[-diff], result.shapeInfo() + 1,
rankOld * sizeof(LongType)); // put old shape numbers at rest of positions
result.reshapei(ordering(), shapeNew);
}
return result; // nothing to do, if diff >= 0 -> identity tile
}
// evaluate shapeInfo for resulting array
auto newShapeInfo = ShapeUtils::evalTileShapeInfo(*this, reps, getContext()->getWorkspace());
// create new buffer, in any case the memory amount new buffer points to is bigger then those for old _buffer
DataBuffer * newBuff = new DataBuffer(shape::length(newShapeInfo) * sizeOfT(),
dataType(), getContext()->getWorkspace(), true);
// assign new shape and new buffer to resulting array
NDArray result(newBuff,const_cast<sd::LongType *>(newShapeInfo) , getContext());
// fill newBuff, loop through all elements of newBuff
// looping through buffer() goes automatically by means of getSubArrayIndex applying
const auto resultLen = result.lengthOf();
auto xType = this->dataType();
auto stream = getContext()->getCudaStream();
prepareSpecialUse({&result}, {this});
BUILD_SINGLE_SELECTOR(xType, tileKernelH,
(this->specialBuffer(), this->specialShapeInfo(), result.specialBuffer(),
result.specialShapeInfo(), resultLen, stream),
SD_COMMON_TYPES);
registerSpecialUse({&result}, {this});
return result;
}
//////////////////////////////////////////////////////////////////////////
// change an array by repeating it the number of times given by reps.
void NDArray::tile(const std::vector<LongType>& reps, NDArray& target) {
auto repProd = shape::prodLong(reps.data(), reps.size());
if (repProd < 1) THROW_EXCEPTION("NDArray::tile: reps can't contain 0s");
// evaluate true tile shapeInfo for comparison with target shapeInfo
auto newShapeInfo = ShapeUtils::evalTileShapeInfo(*this, reps, getContext()->getWorkspace());
if (!shape::equalsSoft(newShapeInfo, target.shapeInfo())) {
THROW_EXCEPTION("NDArray::tile method - shapeInfo of target array is not suitable for tile operation !");
}
// fill newBuff, loop through all elements of newBuff
// looping through buffer() goes automatically by means of getSubArrayIndex applying
const int ews = target.ews();
const int targetLen = target.lengthOf();
auto stream = getContext()->getCudaStream();
prepareSpecialUse({&target}, {this});
BUILD_SINGLE_SELECTOR_TWICE(
target.dataType(), tileKernelHH,
(specialBuffer(), specialShapeInfo(), target.specialBuffer(), target.specialShapeInfo(), targetLen, stream),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this});
}
//////////////////////////////////////////////////////////////////////////
void NDArray::tile(NDArray& target) {
if (rankOf() > target.rankOf())
THROW_EXCEPTION(
"NDArray::tile method - rank of target array must be bigger or equal to the rank of this array !");
if (!ShapeUtils::areShapesBroadcastable(*this, target))
THROW_EXCEPTION("NDArray::tile method - shapeInfo of target array is not suitable for tile operation !");
// fill newBuff, loop through all elements of newBuff
// looping through getBuffer() goes automatically by means of getSubArrayIndex applying
const auto ews = target.ews();
const auto targetLen = target.lengthOf();
auto stream = getContext()->getCudaStream();
prepareSpecialUse({&target}, {this});
BUILD_SINGLE_SELECTOR_TWICE(
target.dataType(), tileKernelHH,
(specialBuffer(), specialShapeInfo(), target.specialBuffer(), target.specialShapeInfo(), targetLen, stream),
SD_COMMON_TYPES);
registerSpecialUse({&target}, {this});
}
////////////////////////////////////////////////////////////////////////
template <typename X, typename Z>
SD_KERNEL static void repeatCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const LongType* repeats, const LongType repSize,
const int axis) {
const X* x = reinterpret_cast<const X*>(vx);
Z* z = reinterpret_cast<Z*>(vz);
__shared__ LongType rank, *sharedMem;
__shared__ LongType zLen, totalThreads; // xLen = zLen
if (threadIdx.x == 0) {
extern __shared__ unsigned char shmem[];
sharedMem = reinterpret_cast<LongType*>(shmem);
rank = shape::rank(zShapeInfo); // xRank = zRank
zLen = shape::length(zShapeInfo); // xLen <= zLen
totalThreads = gridDim.x * blockDim.x;
}
__syncthreads();
auto coords = sharedMem + threadIdx.x * rank;
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
for (LongType i = tid; i < zLen; i += totalThreads) {
INDEX2COORDS(i, rank, shape::shapeOf(zShapeInfo), coords);
LongType zOffset;
COORDS2INDEX(rank, shape::stride(zShapeInfo), coords, zOffset);
if (repSize > 1) {
for (LongType j = 0; j < repSize; ++j) {
coords[axis] -= repeats[j];
if (coords[axis] < 0) {
coords[axis] = j;
break;
}
}
} else
coords[axis] /= repeats[0];
LongType xOffset;
COORDS2INDEX(rank, shape::stride(xShapeInfo), coords, xOffset);
z[zOffset] = x[xOffset];
}
}
//////////////////////////////////////////////////////////////////////////
template <typename X, typename Z>
static void repeatCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const LongType* repeats, const LongType repSize, const LongType axis) {
repeatCuda<X, Z>
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, repeats, repSize, axis);
DebugHelper::checkGlobalErrorCode("NDArray repeat cuda failed(...) failed");
}
BUILD_DOUBLE_TEMPLATE( void repeatCudaLauncher,
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, void* vz,
const sd::LongType* zShapeInfo, const sd::LongType* repeats, const sd::LongType repSize, const sd::LongType axis),
SD_COMMON_TYPES, SD_COMMON_TYPES);
//////////////////////////////////////////////////////////////////////////
// create new array by repeating it the number of times given by repeats
NDArray NDArray::repeat(const int axis, const std::vector<LongType>& repeats) {
auto nonConst = const_cast<NDArray *>(this);
std::vector<sd::LongType> shape = ShapeUtils::evalRepeatShape(axis, repeats, *nonConst);
NDArray output('c',shape, dataType(), getContext());
dim3 launchDims = getRepeatLaunchDims(output.lengthOf(), output.rankOf());
PointersManager manager(getContext(), "NDArray::repeat(const int axis, const std::vector<int>& repeats)");
const LongType* reps = reinterpret_cast<LongType*>(manager.replicatePointer(repeats.data(), repeats.size() * sizeof(LongType)));
prepareSpecialUse({&output}, {this});
BUILD_SINGLE_SELECTOR_TWICE(
dataType(), repeatCudaLauncher,
(launchDims.y, launchDims.x, launchDims.z, getContext()->getCudaStream(), specialBuffer(), specialShapeInfo(),
output.specialBuffer(), output.specialShapeInfo(), reps, repeats.size(), axis),
SD_COMMON_TYPES);
prepareSpecialUse({&output}, {this});
manager.synchronize();
return output;
}
//////////////////////////////////////////////////////////////////////////
// fill array by repeating it the number of times given by repeats
void NDArray::repeat(const int axis, const std::vector<LongType>& repeats, NDArray& target) {
auto nonConst = const_cast<NDArray *>(this);
std::vector<sd::LongType> shape = ShapeUtils::evalRepeatShape(axis, repeats, *nonConst);
if (!target.isSameShape(shape))
THROW_EXCEPTION(
"NDArray::repeat(const int axis, const std::vector<int>& repeats, NDArray& target) method: wrong shape of "
"target array!");
dim3 launchDims = getRepeatLaunchDims(target.lengthOf(), target.rankOf());
PointersManager manager(getContext(), "NDArray::repeat(const int axis, const std::vector<int>& repeats)");
const LongType* reps = reinterpret_cast<LongType*>(manager.replicatePointer(repeats.data(), repeats.size() * sizeof(LongType)));
auto targetDataType = target.dataType();
auto selfDType = dataType();
prepareSpecialUse({&target}, {this});
BUILD_DOUBLE_SELECTOR(
dataType(), target.dataType(), repeatCudaLauncher,
(launchDims.y, launchDims.x, launchDims.z, getContext()->getCudaStream(), specialBuffer(), specialShapeInfo(),
target.specialBuffer(), target.specialShapeInfo(), reps, repeats.size(), axis),
SD_COMMON_TYPES, SD_COMMON_TYPES);
prepareSpecialUse({&target}, {this});
manager.synchronize();
}
////////////////////////////////////////////////////////////////////////
void* NDArray::specialBuffer() {
if (_buffer == nullptr) {
THROW_EXCEPTION("NDArray::specialBuffer(): _buffer is nullptr - array not properly initialized");
}
void* specialBuf = _buffer->special();
if (specialBuf == nullptr) {
syncToDevice();
tickReadHost();
specialBuf = _buffer->special();
if (specialBuf == nullptr) {
THROW_EXCEPTION("NDArray::specialBuffer(): _buffer->special() returned nullptr even after syncToDevice - buffer not allocated");
}
}
// FIXME: this should be fixed once CUDA backend added
return static_cast<int8_t*>(specialBuf) + (offset() * sizeOfT());
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void NDArray::printCurrentBuffer(const bool host, const char* msg, const int precision) {
if (!isScalar() && _length == 0) {
printf("NDArray::printActualBuffer: array length is zero !\n");
return;
}
if(isScalar()) {
if(host) {
if (msg) printf("%s", msg);
if (buffer() == nullptr ) {
printf("NDArray::printActualBuffer: host buffer is nullptr !\n");
return;
}
const T* buff = bufferAsT<T>();
if (msg) printf("%s", msg);
printf("%.*f\n", precision, (double)buff[getOffset(0)]);
return;
} else {
if (msg) printf("%s", msg);
if (specialBuffer() == nullptr) {
printf("NDArray::printSpecialBuffer: special buffer is nullptr !\n");
return;
}
const auto sizeOfBuffer = sizeOfT();
void* pHost = operator new(sizeOfBuffer);
cudaMemcpyAsync(pHost, specialBuffer(), sizeOfBuffer, cudaMemcpyDeviceToHost, *getContext()->getCudaStream());
cudaDeviceSynchronize();
cudaError_t cudaResult = cudaStreamSynchronize(*getContext()->getCudaStream());
auto cast = reinterpret_cast<T*>(pHost);
if (cudaResult != 0) THROW_EXCEPTION("NDArray::printSpecialBuffer: cudaStreamSynchronize failed!");
printf("%.*f\n", precision, (double)cast[0]);
return;
}
}
if (msg) printf("%s", msg);
if (host) {
if (buffer() == nullptr || _length == 0) {
printf("NDArray::printActualBuffer: host buffer is nullptr !\n");
return;
}
const T* buff = bufferAsT<T>();
for (LongType i = 0; i < _length; i++) printf("%.*f, ", precision, (double)buff[getOffset(i)]);
printf("\n");
} else {
if (specialBuffer() == nullptr) {
printf("NDArray::printSpecialBuffer: special buffer is nullptr !\n");
return;
}
const auto sizeOfBuffer = sizeOfT() * (getOffset(_length - 1) + 1);
void* pHost = operator new(sizeOfBuffer);
cudaMemcpyAsync(pHost, specialBuffer(), sizeOfBuffer, cudaMemcpyDeviceToHost, *getContext()->getCudaStream());
cudaError_t cudaResult = cudaStreamSynchronize(*getContext()->getCudaStream());
if (cudaResult != 0) THROW_EXCEPTION("NDArray::printSpecialBuffer: cudaStreamSynchronize failed!");
for (LongType i = 0; i < _length; i++)
printf("%.*f, ", precision, (double)reinterpret_cast<T*>(pHost)[getOffset(i)]);
printf("\n");
operator delete(pHost);
}
}
#define PRINT_BUFFER(T) template void NDArray::printCurrentBuffer<GET_SECOND(T)>(const bool host, const char* msg, const int precision);
ITERATE_LIST((SD_COMMON_TYPES),PRINT_BUFFER)
} // end namespace sd
#endif
+624
View File
@@ -0,0 +1,624 @@
/*
* ******************************************************************************
* *
* *
* * 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/NDArray.h>
#include <exceptions/cuda_exception.h>
#include <execution/ThreadPool.h>
#include <helpers/DebugHelper.h>
#include <loops/legacy_ops.h>
#include <system/Environment.h>
#include <system/op_boilerplate.h>
#include <types/types.h>
#include "helpers/ShapeUtils.h"
namespace sd {
// ----------- Unary Lambda Operations ----------------
template <typename T>
SD_KERNEL void applyLambdaKernel(const void* vx, const sd::LongType* xShapeInfo,
void* vz, const sd::LongType* zShapeInfo,
void* vextraParams) {
// Cast input and output pointers
auto x = reinterpret_cast<const T*>(vx);
auto z = reinterpret_cast<T*>(vz);
auto extraParams = reinterpret_cast<void*>(vextraParams);
// Cache shape information for x buffer
__shared__ sd::LongType length;
__shared__ sd::LongType xRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* xStridePtr;
// Cache shape information for z buffer
__shared__ sd::LongType zRank;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* zStridePtr;
if (threadIdx.x == 0) {
length = shape::length(xShapeInfo);
// Cache x shape information
xRank = shape::rank(xShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
// Cache z shape information
zRank = shape::rank(zShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
int totalThreads = gridDim.x * blockDim.x;
for (sd::LongType i = tid; i < length; i += totalThreads) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType zOffset;
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
// Apply the function using extraParams (this will be handled in the wrapper function)
// For now, using a placeholder
z[zOffset] = x[xOffset]; // This will be replaced with the actual lambda function call
}
}
// ----------- Indexed Lambda Operations ----------------
template <typename T>
SD_KERNEL void applyIndexedLambdaKernel(const void* vx, const sd::LongType* xShapeInfo,
void* vz, const sd::LongType* zShapeInfo,
void* vextraParams) {
// Cast input and output pointers
auto x = reinterpret_cast<const T*>(vx);
auto z = reinterpret_cast<T*>(vz);
auto extraParams = reinterpret_cast<void*>(vextraParams);
// Cache shape information for x buffer
__shared__ sd::LongType length;
__shared__ sd::LongType xRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* xStridePtr;
// Cache shape information for z buffer
__shared__ sd::LongType zRank;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* zStridePtr;
if (threadIdx.x == 0) {
length = shape::length(xShapeInfo);
// Cache x shape information
xRank = shape::rank(xShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
// Cache z shape information
zRank = shape::rank(zShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
int totalThreads = gridDim.x * blockDim.x;
for (sd::LongType i = tid; i < length; i += totalThreads) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType zOffset;
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
// Apply the indexed function - placeholder for actual lambda call
z[zOffset] = x[xOffset]; // This will be replaced with the actual indexed lambda function call
}
}
// ----------- Pairwise Lambda Operations ----------------
template <typename T>
SD_KERNEL void applyPairwiseLambdaKernel(const void* vx, const sd::LongType* xShapeInfo,
const void* vy, const sd::LongType* yShapeInfo,
void* vz, const sd::LongType* zShapeInfo,
void* vextraParams, bool isScalar) {
// Cast input and output pointers
auto x = reinterpret_cast<const T*>(vx);
auto y = reinterpret_cast<const T*>(vy);
auto z = reinterpret_cast<T*>(vz);
auto extraParams = reinterpret_cast<void*>(vextraParams);
// Cache shape information
__shared__ sd::LongType length;
__shared__ sd::LongType xRank;
__shared__ sd::LongType yRank;
__shared__ sd::LongType zRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* yShapePtr;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* xStridePtr;
__shared__ const sd::LongType* yStridePtr;
__shared__ const sd::LongType* zStridePtr;
__shared__ T scalarValue;
__shared__ sd::LongType yOffset0;
if (threadIdx.x == 0) {
length = shape::length(xShapeInfo);
// Cache shape information
xRank = shape::rank(xShapeInfo);
yRank = shape::rank(yShapeInfo);
zRank = shape::rank(zShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
yShapePtr = shape::shapeOf(yShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
yStridePtr = shape::stride(yShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
if (isScalar) {
sd::LongType yCoords[SD_MAX_RANK];
for (int i = 0; i < yRank; i++) {
yCoords[i] = 0;
}
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset0);
scalarValue = y[yOffset0];
}
}
__syncthreads();
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
int totalThreads = gridDim.x * blockDim.x;
for (sd::LongType i = tid; i < length; i += totalThreads) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType zOffset;
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
if (isScalar) {
// Apply the pairwise function with scalar - placeholder
z[zOffset] = x[xOffset]; // Will be replaced with actual function call using scalarValue
} else {
INDEX2COORDS(i, yRank, yShapePtr, yCoords);
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset);
// Apply the pairwise function - placeholder
z[zOffset] = x[xOffset]; // Will be replaced with actual function call using y[yOffset]
}
}
}
// ----------- Indexed Pairwise Lambda Operations ----------------
template <typename T>
SD_KERNEL void applyIndexedPairwiseLambdaKernel(const void* vx, const sd::LongType* xShapeInfo,
const void* vy, const sd::LongType* yShapeInfo,
void* vz, const sd::LongType* zShapeInfo,
void* vextraParams) {
// Cast input and output pointers
auto x = reinterpret_cast<const T*>(vx);
auto y = reinterpret_cast<const T*>(vy);
auto z = reinterpret_cast<T*>(vz);
auto extraParams = reinterpret_cast<void*>(vextraParams);
// Cache shape information
__shared__ sd::LongType length;
__shared__ sd::LongType xRank;
__shared__ sd::LongType yRank;
__shared__ sd::LongType zRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* yShapePtr;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* xStridePtr;
__shared__ const sd::LongType* yStridePtr;
__shared__ const sd::LongType* zStridePtr;
if (threadIdx.x == 0) {
length = shape::length(xShapeInfo);
// Cache shape information
xRank = shape::rank(xShapeInfo);
yRank = shape::rank(yShapeInfo);
zRank = shape::rank(zShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
yShapePtr = shape::shapeOf(yShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
yStridePtr = shape::stride(yShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
int totalThreads = gridDim.x * blockDim.x;
for (sd::LongType i = tid; i < length; i += totalThreads) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType zOffset;
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
INDEX2COORDS(i, yRank, yShapePtr, yCoords);
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset);
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
// Apply the indexed pairwise function - placeholder
z[zOffset] = x[xOffset]; // Will be replaced with actual function call
}
}
// ----------- Triplewise Lambda Operations ----------------
template <typename T>
SD_KERNEL void applyTriplewiseLambdaKernel(const void* vx, const sd::LongType* xShapeInfo,
const void* vy, const sd::LongType* yShapeInfo,
const void* vt, const sd::LongType* tShapeInfo,
void* vz, const sd::LongType* zShapeInfo,
void* vextraParams) {
// Cast input and output pointers
auto x = reinterpret_cast<const T*>(vx);
auto y = reinterpret_cast<const T*>(vy);
auto t = reinterpret_cast<const T*>(vt);
auto z = reinterpret_cast<T*>(vz);
auto extraParams = reinterpret_cast<void*>(vextraParams);
// Cache shape information
__shared__ sd::LongType length;
__shared__ sd::LongType xRank;
__shared__ sd::LongType yRank;
__shared__ sd::LongType tRank;
__shared__ sd::LongType zRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* yShapePtr;
__shared__ const sd::LongType* tShapePtr;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* xStridePtr;
__shared__ const sd::LongType* yStridePtr;
__shared__ const sd::LongType* tStridePtr;
__shared__ const sd::LongType* zStridePtr;
if (threadIdx.x == 0) {
length = shape::length(xShapeInfo);
// Cache shape information
xRank = shape::rank(xShapeInfo);
yRank = shape::rank(yShapeInfo);
tRank = shape::rank(tShapeInfo);
zRank = shape::rank(zShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
yShapePtr = shape::shapeOf(yShapeInfo);
tShapePtr = shape::shapeOf(tShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
yStridePtr = shape::stride(yShapeInfo);
tStridePtr = shape::stride(tShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
int totalThreads = gridDim.x * blockDim.x;
for (sd::LongType i = tid; i < length; i += totalThreads) {
sd::LongType xCoords[SD_MAX_RANK];
sd::LongType yCoords[SD_MAX_RANK];
sd::LongType tCoords[SD_MAX_RANK];
sd::LongType zCoords[SD_MAX_RANK];
sd::LongType xOffset;
sd::LongType yOffset;
sd::LongType tOffset;
sd::LongType zOffset;
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
INDEX2COORDS(i, yRank, yShapePtr, yCoords);
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset);
INDEX2COORDS(i, tRank, tShapePtr, tCoords);
COORDS2INDEX(tRank, tStridePtr, tCoords, tOffset);
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
// Apply the triplewise function - placeholder
z[zOffset] = x[xOffset]; // Will be replaced with actual function call
}
}
// ---------------------- Wrapper functions -----------------------
// Helper class for CUDA Lambda operations
template <typename T>
class NDArrayLambdaCuda {
public:
static int constexpr LAMBDA_THREADS = 256;
static int constexpr LAMBDA_BLOCKS = 512;
// Unary lambda wrapper
static void executeLambda(cudaStream_t* stream, const void* x, const sd::LongType* xShapeInfo,
void* z, const sd::LongType* zShapeInfo, void* extraParams) {
if(stream == nullptr) {
THROW_EXCEPTION("executeLambda: Stream must not be nullptr!");
}
dim3 launchDims(LAMBDA_BLOCKS, LAMBDA_THREADS, 1024);
applyLambdaKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
x, xShapeInfo, z, zShapeInfo, extraParams);
sd::DebugHelper::checkErrorCode(stream, "NDArrayLambdaCuda::executeLambda failed");
}
// Indexed lambda wrapper
static void executeIndexedLambda(cudaStream_t* stream, const void* x, const sd::LongType* xShapeInfo,
void* z, const sd::LongType* zShapeInfo, void* extraParams) {
if(stream == nullptr) {
THROW_EXCEPTION("executeIndexedLambda: Stream must not be nullptr!");
}
dim3 launchDims(LAMBDA_BLOCKS, LAMBDA_THREADS, 1024);
applyIndexedLambdaKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
x, xShapeInfo, z, zShapeInfo, extraParams);
sd::DebugHelper::checkErrorCode(stream, "NDArrayLambdaCuda::executeIndexedLambda failed");
}
// Pairwise lambda wrapper
static void executePairwiseLambda(cudaStream_t* stream, const void* x, const sd::LongType* xShapeInfo,
const void* y, const sd::LongType* yShapeInfo,
void* z, const sd::LongType* zShapeInfo, void* extraParams, bool isScalar) {
dim3 launchDims(LAMBDA_BLOCKS, LAMBDA_THREADS, 1024);
if(stream == nullptr) {
THROW_EXCEPTION("executePairwiseLambda: Stream must not be nullptr!");
}
applyPairwiseLambdaKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, extraParams, isScalar);
sd::DebugHelper::checkErrorCode(stream, "NDArrayLambdaCuda::executePairwiseLambda failed");
}
// Indexed pairwise lambda wrapper
static void executeIndexedPairwiseLambda(cudaStream_t* stream, const void* x, const sd::LongType* xShapeInfo,
const void* y, const sd::LongType* yShapeInfo,
void* z, const sd::LongType* zShapeInfo, void* extraParams) {
dim3 launchDims(LAMBDA_BLOCKS, LAMBDA_THREADS, 1024);
applyIndexedPairwiseLambdaKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, extraParams);
sd::DebugHelper::checkErrorCode(stream, "NDArrayLambdaCuda::executeIndexedPairwiseLambda failed");
}
// Triplewise lambda wrapper
static void executeTriplewiseLambda(cudaStream_t* stream, const void* x, const sd::LongType* xShapeInfo,
const void* y, const sd::LongType* yShapeInfo,
const void* t, const sd::LongType* tShapeInfo,
void* z, const sd::LongType* zShapeInfo, void* extraParams) {
if(stream == nullptr) {
THROW_EXCEPTION("executeTriplewiseLambda: Stream must not be nullptr!");
}
dim3 launchDims(LAMBDA_BLOCKS, LAMBDA_THREADS, 1024);
applyTriplewiseLambdaKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
x, xShapeInfo, y, yShapeInfo, t, tShapeInfo, z, zShapeInfo, extraParams);
sd::DebugHelper::checkErrorCode(stream, "NDArrayLambdaCuda::executeTriplewiseLambda failed");
}
};
// Implementation of the NDArray Lambda methods for CUDA
template <typename T>
SD_LIB_EXPORT void NDArray::applyLambda(std::function<T(T)>& func, NDArray* target) {
// Validate types
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyLambdaCuda<T> method: wrong template parameter T, its type should be the same as type of this "
"array!");
if (dataType() != target->dataType())
THROW_EXCEPTION("NDArray::applyLambdaCuda<T> method: types of this and target array should match!");
// Get device pointers and stream
auto stream = LaunchContext::defaultContext()->getCudaStream(); // Get the CUDA stream
auto x = this->specialBuffer();
auto z = target->specialBuffer();
auto xShapeInfo = this->specialShapeInfo();
auto zShapeInfo = target->specialShapeInfo();
// Create and set up extraParams
void* extraParams = nullptr; // This would hold the function pointer for the lambda
// Execute the CUDA kernel
NDArrayLambdaCuda<T>::executeLambda(stream, x, xShapeInfo, z, zShapeInfo, extraParams);
}
template <typename T>
SD_LIB_EXPORT void NDArray::applyIndexedLambda(std::function<T(sd::LongType, T)>& func, NDArray* target) {
// Validate types
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyIndexedLambdaCuda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != target->dataType())
THROW_EXCEPTION("NDArray::applyIndexedLambdaCuda<T> method: types of this and target array should match!");
// Get device pointers and stream
auto stream = LaunchContext::defaultContext()->getCudaStream(); // Get the CUDA stream
auto x = this->specialBuffer();
auto z = target->specialBuffer();
auto xShapeInfo = this->specialShapeInfo();
auto zShapeInfo = target->specialShapeInfo();
// Create and set up extraParams
void* extraParams = nullptr; // This would hold the function pointer for the lambda
// Execute the CUDA kernel
NDArrayLambdaCuda<T>::executeIndexedLambda(stream, x, xShapeInfo, z, zShapeInfo, extraParams);
}
template <typename T>
SD_LIB_EXPORT void NDArray::applyPairwiseLambda(NDArray* other, std::function<T(T, T)>& func,
NDArray* target) {
// Validate types
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyPairwiseLambdaCuda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != other->dataType() || dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyPairwiseLambdaCuda<T> method: all three arrays (this, other, target) must have the same type!");
// Check for scalar or same length
bool isScalar = other->isScalar();
if (this->lengthOf() != other->lengthOf() && !this->isScalar() && !isScalar) {
THROW_EXCEPTION("applyPairwiseLambdaCuda requires both operands to have the same shape or one to be a scalar");
}
// Get device pointers and stream
auto stream = LaunchContext::defaultContext()->getCudaStream(); // Get the CUDA stream
auto x = this->specialBuffer();
auto y = other->specialBuffer();
auto z = target->specialBuffer();
auto xShapeInfo = this->specialShapeInfo();
auto yShapeInfo = other->specialShapeInfo();
auto zShapeInfo = target->specialShapeInfo();
// Create and set up extraParams
void* extraParams = nullptr; // This would hold the function pointer for the lambda
// Execute the CUDA kernel
NDArrayLambdaCuda<T>::executePairwiseLambda(stream, x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, extraParams, isScalar);
}
template <typename T>
SD_LIB_EXPORT void NDArray::applyIndexedPairwiseLambda(NDArray* other, std::function<T(sd::LongType, T, T)>& func,
NDArray* target) {
// Validate types
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyIndexedPairwiseLambdaCuda<T> method: wrong template parameter T, its type should be the same as "
"type of this array!");
if (dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyIndexedPairwiseLambdaCuda<T> method: types of this and target array should match!");
if (this->lengthOf() != other->lengthOf()) {
THROW_EXCEPTION("applyIndexedPairwiseLambdaCuda requires both operands to have the same shape");
}
// Get device pointers and stream
auto stream = LaunchContext::defaultContext()->getCudaStream(); // Get the CUDA stream
auto x = this->specialBuffer();
auto y = other->specialBuffer();
auto z = target->specialBuffer();
auto xShapeInfo = this->specialShapeInfo();
auto yShapeInfo = other->specialShapeInfo();
auto zShapeInfo = target->specialShapeInfo();
// Create and set up extraParams
void* extraParams = nullptr; // This would hold the function pointer for the lambda
// Execute the CUDA kernel
NDArrayLambdaCuda<T>::executeIndexedPairwiseLambda(stream, x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, extraParams);
}
template <typename T>
SD_LIB_EXPORT void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third,
std::function<T(T, T, T)>& func, NDArray* target) {
// Validate types
if (dataType() != DataTypeUtils::fromT<T>())
THROW_EXCEPTION(
"NDArray::applyTriplewiseLambdaCuda<T> method: wrong template parameter T, its type should be the same as type of "
"this array!");
if (dataType() != second->dataType() || dataType() != third->dataType() || dataType() != target->dataType())
THROW_EXCEPTION(
"NDArray::applyTriplewiseLambdaCuda<T> method: all four arrays (this, second, third, target) should have the "
"same type!");
if (this->lengthOf() != second->lengthOf() || this->lengthOf() != third->lengthOf() || !this->isSameShape(second) ||
!this->isSameShape(third)) {
std::string errorMessage;
errorMessage += "applyTriplewiseLambdaCuda requires all operands to have the same shape\n";
errorMessage += "this shape: " + ShapeUtils::shapeAsString(this->shapeInfo()) + "\n";
errorMessage += "second shape: " + ShapeUtils::shapeAsString(second->shapeInfo()) + "\n";
errorMessage += "third shape: " + ShapeUtils::shapeAsString(third->shapeInfo()) + "\n";
errorMessage += "target shape: " + ShapeUtils::shapeAsString(target->shapeInfo()) + "\n";
THROW_EXCEPTION(errorMessage.c_str());
}
// Get device pointers and stream
auto stream = LaunchContext::defaultContext()->getCudaStream(); // Get the CUDA stream
auto x = this->specialBuffer();
auto y = second->specialBuffer();
auto t = third->specialBuffer();
auto z = target->specialBuffer();
auto xShapeInfo = this->specialShapeInfo();
auto yShapeInfo = second->specialShapeInfo();
auto tShapeInfo = third->specialShapeInfo();
auto zShapeInfo = target->specialShapeInfo();
// Create and set up extraParams
void* extraParams = nullptr; // This would hold the function pointer for the lambda
// Execute the CUDA kernel
NDArrayLambdaCuda<T>::executeTriplewiseLambda(stream, x, xShapeInfo, y, yShapeInfo, t, tShapeInfo,
z, zShapeInfo, extraParams);
}
#define INSTANTIATE_LAMBDA_METHODS(T) template SD_LIB_EXPORT void NDArray::applyLambda( std::function<GET_SECOND(T)(GET_SECOND(T))>& func, NDArray* target);
ITERATE_LIST((SD_COMMON_TYPES),INSTANTIATE_LAMBDA_METHODS);
#define INSTANTIATE_LAMBDA_METHODS_INDEXED(T) template SD_LIB_EXPORT void NDArray::applyIndexedLambda( std::function<GET_SECOND(T)(sd::LongType, GET_SECOND(T))>& func, NDArray* target);
ITERATE_LIST((SD_COMMON_TYPES),INSTANTIATE_LAMBDA_METHODS_INDEXED);
#define INSTANTIATE_LAMBDA_METHODS_PAIRWISE(T) template SD_LIB_EXPORT void NDArray::applyPairwiseLambda(NDArray* other, std::function<GET_SECOND(T)(GET_SECOND(T), GET_SECOND(T))>& func, NDArray* target);
ITERATE_LIST((SD_COMMON_TYPES),INSTANTIATE_LAMBDA_METHODS_PAIRWISE);
#define INSTANTIATE_LAMBDA_METHODS_INDEX_PAIR(T) template SD_LIB_EXPORT void NDArray::applyIndexedPairwiseLambda(NDArray* other, std::function<GET_SECOND(T)(sd::LongType, GET_SECOND(T), GET_SECOND(T))>& func, NDArray* target);
ITERATE_LIST((SD_COMMON_TYPES),INSTANTIATE_LAMBDA_METHODS_INDEX_PAIR);
#define INSTANTIATE_LAMBDA_METHODS_TRIPLE(T) template void NDArray::applyTriplewiseLambda(NDArray* second, NDArray* third, std::function<GET_SECOND(T)(GET_SECOND(T), GET_SECOND(T), GET_SECOND(T))>& func, NDArray* target);
ITERATE_LIST((SD_COMMON_TYPES),INSTANTIATE_LAMBDA_METHODS_TRIPLE);
} // namespace sd
@@ -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
+739
View File
@@ -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
+278
View File
@@ -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
+146
View File
@@ -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
+91
View File
@@ -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
+252
View File
@@ -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