Есть какой-то другой альтернативный подход? Есть. Не знаю, догадался ли я до него сам или нет. К сожалению, после написанию своего варианта кода, я поспешил поспрашивать, какие варианты могут предложить ИИ.
Варианты от DeepSeek были не лучше, а некоторые даже хуже исходного. Например, он сократил одну из реализаций кода ценой переворачивания массива.
// Разворачиваем strides обратно
std::reverse(strides.begin(), strides.end());
А вот одна из альтернативных реализаций от Claude Opus заслуживает внимания. Он обратил внимание, что можно выполнять не две, а одну последовательность умножений!
Если все элементы входного массива не нулевые, то по завершению цикла
acc == ne. А если хотя бы один элемент был нулевой, то в конце можно просто поменять значение ne на ноль. Происходит вновь возвращение к флагу any_zero, но в более умном варианте. Используя эту идею, можно написать следующий код:static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes) {
auto q = sizes.size();
std::vector<int64_t> strides(q);
int64_t acc = 1;
bool any_zero = false;
for (auto sz : std::ranges::views::reverse(sizes)) {
strides[--q] = acc;
acc *= (sz == 0 ? 1 : sz);
any_zero |= sz == 0;
}
const int64_t ne = any_zero ? 0 : acc;
....
}Ассемблерный код:
.LBB1_7:
mov rdx, rsi
mov rsi, qword ptr [r13 - 8]
add r13, -8
mov qword ptr [rcx], rdx
test rsi, rsi
sete dil
cmp rsi, 1
adc rsi, 0
imul rsi, rdx
or al, dil
add rcx, -8
cmp r13, rbp
jne .LBB1_7
xor r13d, r13d
test al, 1
cmove r13, rsi
mov r14, r8
Будет ли код, в котором выполняется в два раза меньше умножений, более быстрым? Моё предсказание – необязательно. Процессор может выполнить два невзаимосвязанных умножения одновременно на разных конвейерах. Я делаю ставку, что мой вариант и вариант от Claude будут работать с практически одинаковой скоростью.