/*******************************************************************************
* Copyright 2020 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License")* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
#include "gpu/intel/include/types.h"
#define LOAD_FLOAT8(prefix, ptr) \
DATA_TO_FLOAT8(prefix, \
BLOCK_TO_DATA8(prefix, \
READ_BLOCK8(prefix, \
(__global BLOCK_T(ALIAS(prefix)) *)(ptr))))
#define STORE_FLOAT8(prefix, ptr, val) \
WRITE_BLOCK8(prefix, (__global BLOCK_T(ALIAS(prefix)) *)(ptr), \
DATA_TO_BLOCK8(prefix, FLOAT_TO_DATA8(prefix, val)))
#define LOAD_DOUBLE8(prefix, ptr) \
DATA_TO_DOUBLE8(prefix, \
BLOCK_TO_DATA8(prefix, \
READ_BLOCK8(prefix, \
(__global BLOCK_T(ALIAS(prefix)) *)(ptr))))
#define STORE_DOUBLE8(prefix, ptr, val) \
WRITE_BLOCK8(prefix, (__global BLOCK_T(ALIAS(prefix)) *)(ptr), \
DATA_TO_BLOCK8(prefix, DOUBLE_TO_DATA8(prefix, val)))
#if DST_DT_F64
#define UP_CASE_DATA DOUBLE
#define COMMON_DATA_T double
#define COMMON_DATA_MAX DBL_MAX
#define COMMON_DATA_ZERO 0.0
#else
#define UP_CASE_DATA FLOAT
#define COMMON_DATA_T float
#define COMMON_DATA_MAX FLT_MAX
#define COMMON_DATA_ZERO 0.0f
#endif
#define COMMON_DATA8_T CONCAT2(COMMON_DATA_T, 8)
#define COMMON_DATA_TO_X(x, y) CONCAT2(DATA_TO_, UP_CASE_DATA)(x, y)
#define COMMON_X_TO_DATA(x, y) CONCAT2(UP_CASE_DATA, _TO_DATA)(x, y)
#define COMMON_LOAD_DATA8(x, y) CONCAT3(LOAD_, UP_CASE_DATA, 8)(x, y)
#define COMMON_STORE_DATA8(x, y, z) CONCAT3(STORE_, UP_CASE_DATA, 8)(x, y, z)
#define VECT_SIZE 8
#define SV (GROUP_SIZE * VECT_SIZE)
#define HAS_TAIL (SOFTMAX_AXIS_SIZE % SV != 0)
#define NUM_BUF ((SOFTMAX_AXIS_SIZE + SV - 1) / SV)
#define SMALL_BUFFER (REPEAT_SUBGRP_BUF_SIZE < THREAD_BUF_SIZE)
#if IS_FWD
int find_axis_offset(int index, int local_id, int subgroup) {
const int group_axis_block = GROUP_SIZE * THREAD_BUF_SIZE * CHANNELS int offset = CHANNELS * THREAD_BUF_SIZE * local_id if (index > 0) { offset += group_axis_block if (index > 0 && subgroup == (SUBGROUPS_REPEATED - 1) && SMALL_BUFFER) {
offset = group_axis_block
+ (CHANNELS * REPEAT_SUBGRP_BUF_SIZE * local_id) }
return offset}
int get_local_off(int subgroup, int subgroup_local_id) {
if (subgroup == (SUBGROUPS_REPEATED - 1)) {
if (SMALL_BUFFER) {
return ((GROUP_SIZE + SUB_GROUP_SIZE * subgroup) * THREAD_BUF_SIZE
+ subgroup_local_id * REPEAT_SUBGRP_BUF_SIZE) } else { // THREAD_BUF_SIZE == REPEAT_SUBGRP_BUF_SIZE i.e. 8 == 8
return ((GROUP_SIZE + SUB_GROUP_SIZE * subgroup + subgroup_local_id)
* THREAD_BUF_SIZE) }
} else {
return 1 }
}
int get_buffer_size(int index, int subgroup, int local_off) {
if (index > 0 && subgroup == (SUBGROUPS_REPEATED - 1)) {
if (SMALL_BUFFER) {
int tail = SOFTMAX_AXIS_SIZE
- ((GROUP_SIZE + subgroup) * THREAD_BUF_SIZE) return ((local_off + REPEAT_SUBGRP_BUF_SIZE) <= SOFTMAX_AXIS_SIZE)
? REPEAT_SUBGRP_BUF_SIZE
: tail % REPEAT_SUBGRP_BUF_SIZE } else {
return ((local_off + THREAD_BUF_SIZE) <= SOFTMAX_AXIS_SIZE)
? THREAD_BUF_SIZE
: SOFTMAX_AXIS_SIZE % THREAD_BUF_SIZE }
} else {
return THREAD_BUF_SIZE }
}
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__attribute__((intel_reqd_sub_group_size(SUB_GROUP_SIZE))) __kernel void
xe_softmax_fwd(__global SRC_DATA_T *src, __global DST_DATA_T *dst,
__global float *src_scale, __global float *dst_scale) {
float scale = 1.0f#if WITH_SRC_SCALES
scale *= src_scale[0]#endif
#if WITH_DST_SCALES
scale /= dst_scale[0]#endif
#if IS_NHWC || IS_BLOCKED
// gws is the combination of mb and axis size
const int group = get_global_id(0) / GROUP_SIZE const int mb = group / CHANNELS_PADDED const int channel_id = group % CHANNELS_PADDED
const int local_id = get_local_id(0) const int subgroup_local_id = get_sub_group_local_id() const int subgroup_id = get_sub_group_id()
int buf_chunk = (local_id / SUB_GROUP_SIZE) * SOFTMAX_BUF COMMON_DATA_T max_ = -COMMON_DATA_MAX COMMON_DATA_T denom_ = COMMON_DATA_ZERO
#if SUBGROUPS_REPEATED // only for NHWC kernel
// total reads should be num_buf x THREAD_BUF_SIZE
COMMON_DATA_T d[2][THREAD_BUF_SIZE]
int num_buf = (subgroup_id < SUBGROUPS_REPEATED) ? 2 : 1
int local_off = get_local_off(subgroup_id, subgroup_local_id)
for (int i = 0 if (i > 0 && local_off >= SOFTMAX_AXIS_SIZE) break
__global SRC_DATA_T *src_copy = src int axis_offset = find_axis_offset(i, subgroup_local_id, subgroup_id) off_t data_off = (off_t)mb * CHANNELS_PADDED * SOFTMAX_AXIS_SIZE
+ axis_offset + channel_id int buf_reads = get_buffer_size(i, subgroup_id, local_off)
src_copy += data_off for (int k = 0, axis_channel_id = CHANNELS * buf_chunk ++k, axis_channel_id += CHANNELS) {
d[i][k] = COMMON_DATA_TO_X(SRC, src_copy[axis_channel_id]) max_ = max(d[i][k], max_) }
}
#if GROUP_SIZE == SUB_GROUP_SIZE
max_ = sub_group_reduce_max(max_)#else
max_ = work_group_reduce_max(max_)#endif
for (int i = 0 if (i > 0 && local_off >= SOFTMAX_AXIS_SIZE) break
int buf_reads = get_buffer_size(i, subgroup_id, local_off) for (int k = 0#if LOGSOFTMAX
denom_ += exp(d[i][k] - max_)#else
d[i][k] = exp(d[i][k] - max_) denom_ += d[i][k]#endif
}
}
#if GROUP_SIZE == SUB_GROUP_SIZE
denom_ = sub_group_reduce_add(denom_)#else
denom_ = work_group_reduce_add(denom_)#endif
#if LOGSOFTMAX
denom_ = log(denom_)#else
denom_ = (SOFTMAX_INF_AS_ZERO && denom_ == 0.f) ? 1.0f : 1.0f / denom_#endif
for (int i = 0 if (i > 0 && local_off >= SOFTMAX_AXIS_SIZE) break
__global DST_DATA_T *dst_copy = dst int axis_offset = find_axis_offset(i, subgroup_local_id, subgroup_id) off_t data_off = (off_t)mb * CHANNELS_PADDED * SOFTMAX_AXIS_SIZE
+ axis_offset + channel_id int buf_reads = get_buffer_size(i, subgroup_id, local_off) dst_copy += data_off for (int k = 0, axis_channel_id = CHANNELS * buf_chunk ++k, axis_channel_id += CHANNELS) {
#if LOGSOFTMAX
d[i][k] = d[i][k] - max_ - denom_#else
d[i][k] = d[i][k] * denom_#endif
dst_copy[axis_channel_id] = COMMON_X_TO_DATA(DST, d[i][k] * scale) }
}
#else
// NHWC kernel for lws size < max_lws or multiples of max_lws
// Blocked layout kernel for 128-byte reads and writes
const int channel_offset = CHANNELS * THREAD_BUF_SIZE * subgroup_local_id
#if IS_BLOCKED
const int channel_block = channel_id / CHANNELS const int channel_in_block = channel_id % CHANNELS
off_t data_off = (off_t)mb * CHANNELS * SOFTMAX_AXIS_SIZE + channel_offset
+ channel_in_block const int buf_reads = THREAD_BUF_SIZE#else
off_t data_off = (off_t)mb * CHANNELS_PADDED * SOFTMAX_AXIS_SIZE
+ channel_offset + channel_id
const int local_off = local_id * THREAD_BUF_SIZE int buf_reads if (local_off >= SOFTMAX_AXIS_SIZE) {
buf_reads = 0 } else {
buf_reads = ((local_off + THREAD_BUF_SIZE) <= SOFTMAX_AXIS_SIZE)
? THREAD_BUF_SIZE
: (SOFTMAX_AXIS_SIZE % THREAD_BUF_SIZE) }
#endif
COMMON_DATA_T d[THREAD_BUF_SIZE] src += data_off for (int k = 0, axis_channel_id = CHANNELS * buf_chunk ++k, axis_channel_id += CHANNELS) {
d[k] = COMMON_DATA_TO_X(SRC, src[axis_channel_id]) max_ = max(d[k], max_) }
#if GROUP_SIZE == SUB_GROUP_SIZE
max_ = sub_group_reduce_max(max_)#else
max_ = work_group_reduce_max(max_)#endif
for (int k = 0#if LOGSOFTMAX
denom_ += exp(d[k] - max_)#else
d[k] = exp(d[k] - max_) denom_ += d[k]#endif
}
#if GROUP_SIZE == SUB_GROUP_SIZE
denom_ = sub_group_reduce_add(denom_)#else
denom_ = work_group_reduce_add(denom_)#endif
#if LOGSOFTMAX
denom_ = log(denom_)#else
denom_ = (SOFTMAX_INF_AS_ZERO && denom_ == 0.f) ? 1.0f : 1.0f / denom_#endif
dst += data_off
for (int k = 0, axis_channel_id = CHANNELS * buf_chunk ++k, axis_channel_id += CHANNELS) {
#if LOGSOFTMAX
d[k] = d[k] - max_ - denom_#else
d[k] = d[k] * denom_#endif
dst[axis_channel_id] = COMMON_X_TO_DATA(DST, d[k] * scale) }
#endif
#else // NCHW kernel starts here
const off_t data_off
= (off_t)(get_global_id(0) / GROUP_SIZE) * SOFTMAX_AXIS_SIZE
COMMON_DATA8_T d[NUM_BUF] COMMON_DATA_T max_ = -COMMON_DATA_MAX COMMON_DATA_T denom_ = COMMON_DATA_ZERO
int last_buf = HAS_TAIL ? (NUM_BUF - 1) : NUM_BUF
src += data_off int sid = get_sub_group_id()
#if IS_READ_ALIGNED
for (int k = 0#if GROUP_SIZE == SUB_GROUP_SIZE
int idx = k * SUB_GROUP_SIZE#else
int idx = HAS_TAIL ? k * SUB_GROUP_SIZE : sid * SUB_GROUP_SIZE#endif
d[k] = COMMON_LOAD_DATA8(SRC, &src[idx * VECT_SIZE]) for (int i = 0 max_ = max(d[k][i], max_) }
}
#if HAS_TAIL
{
int k = last_buf for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() d[k][i] = (off < SOFTMAX_AXIS_SIZE ? COMMON_DATA_TO_X(SRC, src[off])
: -COMMON_DATA_MAX) max_ = max(d[k][i], max_) }
}
#endif
#else // subgroup block read requires 4-byte alignment
for (int k = 0 for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() d[k][i] = (off < SOFTMAX_AXIS_SIZE ? DATA_TO_FLOAT(SRC, src[off])
: -FLT_MAX) max_ = max(d[k][i], max_) }
}
#endif
#if GROUP_SIZE == SUB_GROUP_SIZE
max_ = sub_group_reduce_max(max_)#else
max_ = work_group_reduce_max(max_)#endif
for (int k = 0#if LOGSOFTMAX
for (int i = 0 denom_ += exp(d[k][i] - max_)#else
d[k] = exp(d[k] - max_) for (int i = 0 denom_ += d[k][i]#endif
}
#if HAS_TAIL
{
int k = last_buf#if LOGSOFTMAX
for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() if (off < SOFTMAX_AXIS_SIZE) denom_ += exp(d[k][i] - max_) }
#else
d[k] = exp(d[k] - max_) for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() if (off < SOFTMAX_AXIS_SIZE) denom_ += d[k][i] }
#endif
}
#endif
#if GROUP_SIZE == SUB_GROUP_SIZE
denom_ = sub_group_reduce_add(denom_)#else
denom_ = work_group_reduce_add(denom_)#endif
#if LOGSOFTMAX
denom_ = log(denom_)#else
denom_ = (SOFTMAX_INF_AS_ZERO && denom_ == 0.f) ? 1.0f : 1.0f / denom_#endif
dst += data_off#if IS_WRITE_ALIGNED
for (int k = 0#if GROUP_SIZE == SUB_GROUP_SIZE
int idx = k * SUB_GROUP_SIZE#else
int idx = HAS_TAIL ? k * SUB_GROUP_SIZE : sid * SUB_GROUP_SIZE#endif
#if LOGSOFTMAX
d[k] = d[k] - max_ - denom_#else
d[k] = d[k] * denom_#endif
COMMON_STORE_DATA8(DST, &dst[idx * VECT_SIZE], scale * d[k]) }
#if HAS_TAIL // subgroup block write requires 16-byte alignment
{
int k = last_buf#if LOGSOFTMAX
d[k] = d[k] - max_ - denom_#else
d[k] = d[k] * denom_#endif
for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() if (off < SOFTMAX_AXIS_SIZE)
dst[off] = COMMON_X_TO_DATA(DST, scale * d[k][i]) }
}
#endif
#else // for test-cases not aligned by 16 bytes needed for block write
for (int k = 0#if LOGSOFTMAX
d[k] = d[k] - max_ - denom_#else
d[k] = d[k] * denom_#endif
for (int i = 0 int off = k * VECT_SIZE * SUB_GROUP_SIZE + i * SUB_GROUP_SIZE
+ get_sub_group_local_id() if (off < SOFTMAX_AXIS_SIZE)
dst[off] = COMMON_X_TO_DATA(DST, scale * d[k][i]) }
}
#endif
#endif
}
#endif
#if IS_BWD
__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1)))
__attribute__((intel_reqd_sub_group_size(SUB_GROUP_SIZE))) __kernel void
xe_softmax_bwd(__global DST_DATA_T *dst, __global SRC_DATA_T *diff_src,
__global DST_DATA_T *diff_dst) {
#if IS_NHWC || IS_16C
const int groups = get_global_id(0) / GROUP_SIZE const int batch = groups / IC_PADDED const int ic = groups % IC_PADDED const int sub_grp_id = get_sub_group_local_id() const int local_id = get_local_id(0) const int slice = local_id / SUB_GROUP_SIZE const int ic_buff = IC * VECT_SIZE
#if IS_16C
const int ic_blk = ic / IC const int ic_in_blk = ic % IC off_t data_off = (off_t)BATCH * IC * SOFTMAX_AXIS_SIZE * ic_blk
+ (off_t)batch * IC * SOFTMAX_AXIS_SIZE + ic_buff * sub_grp_id
+ ic_in_blk#else
off_t data_off
= (off_t)batch * IC * SOFTMAX_AXIS_SIZE + ic_buff * sub_grp_id + ic#endif
COMMON_DATA_T sbr = COMMON_DATA_ZERO COMMON_DATA_T diff_d[VECT_SIZE] COMMON_DATA_T dst_[VECT_SIZE]
diff_dst += data_off dst += data_off
for (int i = 0, idx = IC * slice * SOFTMAX_BUF ++i, idx += IC) {
diff_d[i] = COMMON_DATA_TO_X(DST, diff_dst[idx]) dst_[i] = COMMON_DATA_TO_X(DST, dst[idx])#if LOGSOFTMAX
sbr += diff_d[i]#else
sbr += dst_[i] * diff_d[i]#endif
}
#if GROUP_SIZE == SUB_GROUP_SIZE
sbr = sub_group_reduce_add(sbr)#else
sbr = work_group_reduce_add(sbr)#endif
diff_src += data_off
for (int i = 0, idx = IC * slice * SOFTMAX_BUF ++i, idx += IC) {
#if LOGSOFTMAX
diff_d[i] = diff_d[i] - exp(dst_[i]) * sbr#else
diff_d[i] = (diff_d[i] - sbr) * dst_[i]#endif
diff_src[idx] = COMMON_X_TO_DATA(SRC, diff_d[i]) }
#else
const off_t data_off
= (off_t)(get_global_id(0) / GROUP_SIZE) * SOFTMAX_AXIS_SIZE
COMMON_DATA_T sbr = COMMON_DATA_ZERO COMMON_DATA8_T diff_d[NUM_BUF] COMMON_DATA8_T dst_[NUM_BUF]
diff_dst += data_off dst += data_off for (int k = 0 diff_d[k] = COMMON_LOAD_DATA8(
DST, &diff_dst[k * VECT_SIZE * SUB_GROUP_SIZE]) dst_[k] = COMMON_LOAD_DATA8(DST, &dst[k * VECT_SIZE * SUB_GROUP_SIZE])
for (int i = 0#if LOGSOFTMAX
sbr += diff_d[k][i]#else
sbr += dst_[k][i] * diff_d[k][i]#endif
}
}
sbr = sub_group_reduce_add(sbr)
diff_src += data_off
for (int k = 0#if LOGSOFTMAX
diff_d[k] = diff_d[k] - exp(dst_[k]) * sbr#else
diff_d[k] = (diff_d[k] - sbr) * dst_[k]#endif
COMMON_STORE_DATA8(
SRC, &diff_src[k * VECT_SIZE * SUB_GROUP_SIZE], diff_d[k]) }
#endif
}
#endif