aclnnXlog1py
产品支持情况
| 产品 | 是否支持 |
|---|---|
| Ascend 950PR/Ascend 950DT | √ |
| Atlas A3 训练系列产品/Atlas A3 推理系列产品 | × |
| Atlas A2 训练系列产品/Atlas A2 推理系列产品 | × |
| Atlas 200I/500 A2 推理产品 | × |
| Atlas 推理系列产品 | × |
| Atlas 训练系列产品 | × |
功能说明
-
接口功能:计算x * log(1 + y),当x == 0时结果为0。
-
计算公式:
zi={0,xi=0xi⋅log(1+yi),xi≠0z_i = \begin{cases} 0, & x_i = 0 \\ x_i \cdot \log(1 + y_i), & x_i \neq 0 \end{cases}
- x与y支持broadcast,输出shape为广播后的最大值shape。
- y为NaN时输出y原值。
函数原型
每个算子分为两段式接口,必须先调用"aclnnXlog1pyGetWorkspaceSize"接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用"aclnnXlog1py"接口执行计算。
aclnnStatus aclnnXlog1pyGetWorkspaceSize(
const aclTensor *x,
const aclTensor *y,
const aclTensor *z,
uint64_t *workspaceSize,
aclOpExecutor **executor)
aclnnStatus aclnnXlog1py(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)
aclnnXlog1pyGetWorkspaceSize
-
参数说明:
参数名 输入/输出 描述 使用说明 数据类型 数据格式 维度(shape) 非连续Tensor x 输入 表示乘数因子,对应公式中x。 - 支持空Tensor。
- 数据类型需与y、z保持一致。
- shape需与y满足broadcast关系。
FLOAT、FLOAT16、BFLOAT16 ND 0-8 √ y 输入 表示log1p的自变量,对应公式中y。 - 支持空Tensor。
- 数据类型需与x保持一致。
- shape需与x满足broadcast关系。
数据类型与x保持一致。 ND 0-8 √ z 输出 表示计算结果,对应公式中z。 shape需为x与y broadcast后的shape。 数据类型与x保持一致。 ND 1-8 √ workspaceSize 输出 返回需要在Device侧申请的workspace大小。 - - - - - executor 输出 返回op执行器,包含了算子计算流程。 - - - - - -
返回值:
aclnnStatus:返回状态码,具体参见aclnn返回码。
第一段接口会完成入参校验,出现以下场景时报错:
返回码 错误码 描述 ACLNN_ERR_PARAM_NULLPTR 161001 传入的tensor是空指针。 ACLNN_ERR_PARAM_INVALID 161002 x、y或z的数据类型和数据格式不在支持的范围之内。 x、y、z的数据类型不匹配。 x、y或z的shape维度不在支持的范围之内(最大8维)。 x、y或z使用了私有格式(private format)。
aclnnXlog1py
-
参数说明:
参数名 输入/输出 描述 workspace 输入 在Device侧申请的workspace内存地址。 workspaceSize 输入 在Device侧申请的workspace大小,由第一段接口aclnnXlog1pyGetWorkspaceSize获取。 executor 输入 op执行器,包含了算子计算流程。 stream 输入 指定执行任务的Stream。 -
返回值:
aclnnStatus:返回状态码,具体参见aclnn返回码。
约束说明
- 确定性计算:
- aclnnXlog1py默认确定性实现。
- 输入数据类型必须为FLOAT、FLOAT16或BFLOAT16。
- shape维度范围为0-8。
- 不支持私有格式(private format),仅支持ND格式。
- 输入x、y,输出z的数据类型必须一致。
调用示例
示例代码如下,仅供参考,具体编译和执行过程请参考编译与运行样例。
#include <iostream>
#include <vector>
#include <cmath>
#include "acl/acl.h"
#include "aclnnop/aclnn_xlog1py.h"
#define CHECK_RET(cond, msg) \
do { \
if (!(cond)) { \
printf("[FAIL] " msg "\n"); \
return -1; \
} \
} while (0)
#define LOG_PRINT(msg, ...) printf(msg "\n", ##__VA_ARGS__)
int64_t GetShapeSize(const std::vector<int64_t>& shape)
{
int64_t size = 1;
for (auto i : shape) size *= i;
return size;
}
// Broadcast index: map flat index in output to flat index in input
static int64_t BroadcastIdx(int64_t flat, const std::vector<int64_t>& inShape,
const std::vector<int64_t>& outShape)
{
int inRank = (int)inShape.size();
int outRank = (int)outShape.size();
int64_t outIdx = 0, outStride = 1;
for (int d = 0; d < outRank; d++) {
int dimIdx = outRank - 1 - d;
int64_t dim = outShape[dimIdx];
int64_t coord = (flat / outStride) % dim;
int inDimIdx = dimIdx - (outRank - inRank);
int64_t inDim = (inDimIdx >= 0) ? inShape[inDimIdx] : 1;
int64_t inCoord = (inDim == 1) ? 0 : coord;
int inStride = 1;
for (int dd = inRank - 1; dd > inDimIdx; dd--) inStride *= inShape[dd];
outIdx += inCoord * inStride;
outStride *= dim;
}
return outIdx;
}
std::vector<float> ComputeGolden(
const std::vector<float>& x, const std::vector<int64_t>& shapeX,
const std::vector<float>& y, const std::vector<int64_t>& shapeY,
const std::vector<int64_t>& outShape)
{
int64_t n = GetShapeSize(outShape);
std::vector<float> result(n);
for (int64_t i = 0; i < n; i++) {
int64_t ix = BroadcastIdx(i, shapeX, outShape);
int64_t iy = BroadcastIdx(i, shapeY, outShape);
float fx = x[ix], fy = y[iy];
if (fx == 0.0f) {
result[i] = 0.0f;
} else {
result[i] = fx * std::log1p(fy);
}
}
return result;
}
int Init(int32_t deviceId, aclrtStream* stream)
{
auto ret = aclInit(nullptr);
CHECK_RET(ret == ACL_SUCCESS, "aclInit failed");
ret = aclrtSetDevice(deviceId);
CHECK_RET(ret == ACL_SUCCESS, "aclrtSetDevice failed");
ret = aclrtCreateStream(stream);
CHECK_RET(ret == ACL_SUCCESS, "aclrtCreateStream failed");
return 0;
}
template<typename T>
int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape,
void** deviceAddr, aclDataType dataType, aclTensor** tensor)
{
auto size = GetShapeSize(shape) * sizeof(T);
auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, "aclrtMalloc failed");
ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
CHECK_RET(ret == ACL_SUCCESS, "aclrtMemcpy H2D failed");
std::vector<int64_t> strides(shape.size(), 1);
for (int64_t i = shape.size() - 2; i >= 0; i--) {
strides[i] = shape[i + 1] * strides[i + 1];
}
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType,
strides.data(), 0, aclFormat::ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
int RunXlog1py(const std::vector<int64_t>& shapeX, const std::vector<float>& dataX,
const std::vector<int64_t>& shapeY, const std::vector<float>& dataY,
const std::string& tag, aclrtStream stream)
{
LOG_PRINT("--- Test %s ---", tag.c_str());
// Compute broadcast output shape
int rank = std::max(shapeX.size(), shapeY.size());
std::vector<int64_t> outShape(rank);
for (int d = 0; d < rank; d++) {
int dx = d - (rank - shapeX.size());
int dy = d - (rank - shapeY.size());
auto sx = (dx >= 0) ? shapeX[dx] : 1;
auto sy = (dy >= 0) ? shapeY[dy] : 1;
outShape[d] = std::max(sx, sy);
}
int64_t outSize = GetShapeSize(outShape);
// Compute golden
auto golden = ComputeGolden(dataX, shapeX, dataY, shapeY, outShape);
// Allocate device tensors
aclTensor* aclX = nullptr; void* devX = nullptr;
auto ret = CreateAclTensor(dataX, shapeX, &devX, aclDataType::ACL_FLOAT, &aclX);
CHECK_RET(ret == 0, "create tensor X failed");
aclTensor* aclY = nullptr; void* devY = nullptr;
ret = CreateAclTensor(dataY, shapeY, &devY, aclDataType::ACL_FLOAT, &aclY);
CHECK_RET(ret == 0, "create tensor Y failed");
std::vector<float> outHostData(outSize, 0);
aclTensor* aclOut = nullptr; void* devOut = nullptr;
ret = CreateAclTensor(outHostData, outShape, &devOut, aclDataType::ACL_FLOAT, &aclOut);
CHECK_RET(ret == 0, "create tensor Out failed");
// Phase 1: GetWorkspaceSize
uint64_t workspaceSize = 0;
aclOpExecutor* executor = nullptr;
ret = aclnnXlog1pyGetWorkspaceSize(aclX, aclY, aclOut, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, "aclnnXlog1pyGetWorkspaceSize failed");
void* workspaceAddr = nullptr;
if (workspaceSize > 0) {
ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, "allocate workspace failed");
}
// Phase 2: Execute
ret = aclnnXlog1py(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, "aclnnXlog1py execute failed");
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, "aclrtSynchronizeStream failed");
// Copy result back and compare
std::vector<float> npuResult(outSize, 0);
ret = aclrtMemcpy(npuResult.data(), outSize * sizeof(float), devOut,
outSize * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST);
CHECK_RET(ret == ACL_SUCCESS, "copy result D2H failed");
bool pass = true;
for (int64_t i = 0; i < outSize; i++) {
float g = golden[i];
float r = npuResult[i];
double mere = (std::fabs(g) > 1e-6) ?
std::fabs(r - g) / std::fabs(g) : std::fabs(r - g);
if (mere > 0.001) {
LOG_PRINT(" [FAIL][%s][%ld] golden=%.6f npu=%.6f", tag.c_str(), i, g, r);
pass = false;
}
}
if (pass) LOG_PRINT(" [PASS][%s] all %ld elems OK", tag.c_str(), outSize);
// Cleanup
aclDestroyTensor(aclX); aclDestroyTensor(aclY); aclDestroyTensor(aclOut);
aclrtFree(devX); aclrtFree(devY); aclrtFree(devOut);
if (workspaceSize > 0) aclrtFree(workspaceAddr);
return pass ? 0 : -1;
}
int main()
{
int32_t deviceId = 0;
aclrtStream stream;
auto ret = Init(deviceId, &stream);
CHECK_RET(ret == 0, "Init failed");
int numPass = 0, numFail = 0;
// Test 1: same shape [1,2,4,4]
{
std::vector<int64_t> shape = {1, 2, 4, 4};
std::vector<float> x(32), y(32);
for (int i = 0; i < 32; i++) { x[i] = 2.0f; y[i] = 1.0f; }
if (RunXlog1py(shape, x, shape, y, "same_shape", stream) == 0) numPass++; else numFail++;
}
// Test 2: broadcast x=[1,2,1,4] y=[1,2,4,4]
{
std::vector<int64_t> shapeX = {1, 2, 1, 4};
std::vector<int64_t> shapeY = {1, 2, 4, 4};
std::vector<float> x(8), y(32);
for (int i = 0; i < 8; i++) x[i] = 3.0f;
for (int i = 0; i < 32; i++) y[i] = 2.0f;
if (RunXlog1py(shapeX, x, shapeY, y, "broadcast", stream) == 0) numPass++; else numFail++;
}
// Test 3: x == 0 boundary case
{
std::vector<int64_t> shape = {1, 1, 8, 8};
std::vector<float> x(64, 0.0f);
std::vector<float> y(64, 100.0f);
if (RunXlog1py(shape, x, shape, y, "x_eq_0", stream) == 0) numPass++; else numFail++;
}
LOG_PRINT("========================================");
LOG_PRINT("ACLNN Xlog1py NPU results: PASS=%d FAIL=%d", numPass, numFail);
LOG_PRINT("========================================");
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return (numFail == 0) ? 0 : -1;
}