Download native_relative_linear.cpp from mussolene/relative-int4-research-prototype: direct link, hf CLI and curl.
- Browser
- Download file 26.8 kB
-
https://huggingface.co/mussolene/relative-int4-research-prototype/resolve/main/native_relative_linear.cpp
- Command line
-
hf download hf://mussolene/relative-int4-research-prototype/native_relative_linear.cpp
-
curl -L -o native_relative_linear.cpp https://huggingface.co/mussolene/relative-int4-research-prototype/resolve/main/native_relative_linear.cpp
26.8 kB
| // Fused packed W4A32 reference execution. No full decoded weight buffer. | |
| // Sparse connectivity reduces dot products; INT4 alone does not reduce MAC count. | |
| struct Layer { | |
| int din, dout, width, group, rank, mode; | |
| const float *center, *unit, *bias, *weight, *scales, *a, *b; | |
| const uint8_t *codes; | |
| const uint32_t *indices; | |
| }; | |
| static bool zero_center(const Layer& l) { | |
| if (!l.center) return true; | |
| for (int i = 0; i < l.din; ++i) if (l.center[i] != 0.0f) return false; | |
| return true; | |
| } | |
| extern "C" int relative_integer_dot_supported() { | |
| return 1; | |
| return 0; | |
| } | |
| extern "C" int relative_gated_delta_supported() { | |
| return 1; | |
| return 0; | |
| } | |
| // CPU streaming equivalent of mlx_lm.models.gated_delta.gated_delta_ops. | |
| // State rows stay local to one (batch, head, value-dimension) sequence row. | |
| extern "C" int relative_gated_delta(const float* q, const float* k, | |
| const float* v, const float* g, | |
| const float* beta, const float* state_in, | |
| const uint8_t* mask, int batch, int time, | |
| int hk_count, int hv_count, int dk, int dv, | |
| int vector_gate, int has_mask, float* y, | |
| float* state_out) { | |
| if (batch < 1 || time < 1 || hk_count < 1 || hv_count < 1 || dk < 1 || dv < 1 || | |
| hv_count % hk_count || (vector_gate != 0 && vector_gate != 1) || | |
| (has_mask != 0 && has_mask != 1) || !q || !k || !v || !g || !beta || | |
| !state_in || !y || !state_out || (has_mask && !mask)) return 1; | |
| if (!relative_gated_delta_supported()) return 2; | |
| return 2; | |
| const int repeat = hv_count / hk_count; | |
| const size_t state_row_size = static_cast<size_t>(dk); | |
| std::vector<float> row(state_row_size), candidate(state_row_size); | |
| for (int b = 0; b < batch; ++b) { | |
| for (int h = 0; h < hv_count; ++h) { | |
| const int hk = h / repeat; | |
| for (int d = 0; d < dv; ++d) { | |
| const size_t state_offset = | |
| ((static_cast<size_t>(b) * hv_count + h) * dv + d) * dk; | |
| std::copy(state_in + state_offset, state_in + state_offset + dk, row.begin()); | |
| for (int t = 0; t < time; ++t) { | |
| const size_t q_offset = | |
| ((static_cast<size_t>(b) * time + t) * hk_count + hk) * dk; | |
| const size_t v_offset = | |
| (static_cast<size_t>(b) * time + t) * hv_count * dv + | |
| static_cast<size_t>(h) * dv + d; | |
| const size_t gate_offset = | |
| (static_cast<size_t>(b) * time + t) * hv_count + h; | |
| const float* kt = k + q_offset; | |
| const float* qt = q + q_offset; | |
| float kv_mem = 0.0f; | |
| float32x4_t kv0 = vdupq_n_f32(0.0f), kv1 = kv0; | |
| float32x4_t kv2 = kv0, kv3 = kv0, kv_tail = kv0; | |
| int i = 0; | |
| for (; i + 16 <= dk; i += 16) { | |
| float32x4_t gate0, gate1, gate2, gate3; | |
| if (vector_gate) { | |
| const float* gt = g + gate_offset * dk + i; | |
| gate0 = vld1q_f32(gt); | |
| gate1 = vld1q_f32(gt + 4); | |
| gate2 = vld1q_f32(gt + 8); | |
| gate3 = vld1q_f32(gt + 12); | |
| } else { | |
| const float32x4_t gate = vdupq_n_f32(g[gate_offset]); | |
| gate0 = gate1 = gate2 = gate3 = gate; | |
| } | |
| const float* rt = row.data() + i; | |
| const float32x4_t c0 = vmulq_f32(vld1q_f32(rt), gate0); | |
| const float32x4_t c1 = vmulq_f32(vld1q_f32(rt + 4), gate1); | |
| const float32x4_t c2 = vmulq_f32(vld1q_f32(rt + 8), gate2); | |
| const float32x4_t c3 = vmulq_f32(vld1q_f32(rt + 12), gate3); | |
| float* ct = candidate.data() + i; | |
| vst1q_f32(ct, c0); vst1q_f32(ct + 4, c1); | |
| vst1q_f32(ct + 8, c2); vst1q_f32(ct + 12, c3); | |
| kv0 = vaddq_f32(kv0, vmulq_f32(c0, vld1q_f32(kt + i))); | |
| kv1 = vaddq_f32(kv1, vmulq_f32(c1, vld1q_f32(kt + i + 4))); | |
| kv2 = vaddq_f32(kv2, vmulq_f32(c2, vld1q_f32(kt + i + 8))); | |
| kv3 = vaddq_f32(kv3, vmulq_f32(c3, vld1q_f32(kt + i + 12))); | |
| } | |
| for (; i + 4 <= dk; i += 4) { | |
| float32x4_t gate; | |
| if (vector_gate) gate = vld1q_f32(g + gate_offset * dk + i); | |
| else gate = vdupq_n_f32(g[gate_offset]); | |
| const float32x4_t decayed = | |
| vmulq_f32(vld1q_f32(row.data() + i), gate); | |
| vst1q_f32(candidate.data() + i, decayed); | |
| kv_tail = vaddq_f32(kv_tail, | |
| vmulq_f32(decayed, vld1q_f32(kt + i))); | |
| } | |
| kv_mem = vaddvq_f32(vaddq_f32(vaddq_f32(kv0, kv1), | |
| vaddq_f32(kv2, kv3))) + | |
| vaddvq_f32(kv_tail); | |
| for (; i < dk; ++i) { | |
| const float gate = g[vector_gate ? gate_offset * dk + i : gate_offset]; | |
| candidate[i] = row[i] * gate; | |
| kv_mem += candidate[i] * kt[i]; | |
| } | |
| const float delta = (v[v_offset] - kv_mem) * beta[gate_offset]; | |
| float32x4_t out0 = vdupq_n_f32(0.0f), out1 = out0; | |
| float32x4_t out2 = out0, out3 = out0, out_tail = out0; | |
| i = 0; | |
| const float32x4_t delta4 = vdupq_n_f32(delta); | |
| for (; i + 16 <= dk; i += 16) { | |
| float* ct = candidate.data() + i; | |
| const float32x4_t c0 = vaddq_f32(vld1q_f32(ct), | |
| vmulq_f32(vld1q_f32(kt + i), delta4)); | |
| const float32x4_t c1 = vaddq_f32(vld1q_f32(ct + 4), | |
| vmulq_f32(vld1q_f32(kt + i + 4), delta4)); | |
| const float32x4_t c2 = vaddq_f32(vld1q_f32(ct + 8), | |
| vmulq_f32(vld1q_f32(kt + i + 8), delta4)); | |
| const float32x4_t c3 = vaddq_f32(vld1q_f32(ct + 12), | |
| vmulq_f32(vld1q_f32(kt + i + 12), delta4)); | |
| vst1q_f32(ct, c0); vst1q_f32(ct + 4, c1); | |
| vst1q_f32(ct + 8, c2); vst1q_f32(ct + 12, c3); | |
| out0 = vaddq_f32(out0, vmulq_f32(c0, vld1q_f32(qt + i))); | |
| out1 = vaddq_f32(out1, vmulq_f32(c1, vld1q_f32(qt + i + 4))); | |
| out2 = vaddq_f32(out2, vmulq_f32(c2, vld1q_f32(qt + i + 8))); | |
| out3 = vaddq_f32(out3, vmulq_f32(c3, vld1q_f32(qt + i + 12))); | |
| } | |
| for (; i + 4 <= dk; i += 4) { | |
| const float32x4_t c = vaddq_f32(vld1q_f32(candidate.data() + i), | |
| vmulq_f32(vld1q_f32(kt + i), delta4)); | |
| vst1q_f32(candidate.data() + i, c); | |
| out_tail = vaddq_f32(out_tail, | |
| vmulq_f32(c, vld1q_f32(qt + i))); | |
| } | |
| float out = vaddvq_f32(vaddq_f32(vaddq_f32(out0, out1), | |
| vaddq_f32(out2, out3))) + | |
| vaddvq_f32(out_tail); | |
| for (; i < dk; ++i) { | |
| candidate[i] += kt[i] * delta; | |
| out += candidate[i] * qt[i]; | |
| } | |
| const size_t y_offset = | |
| (static_cast<size_t>(b) * time + t) * hv_count * dv + | |
| static_cast<size_t>(h) * dv + d; | |
| y[y_offset] = out; | |
| if (!has_mask || mask[static_cast<size_t>(b) * time + t]) | |
| row.swap(candidate); | |
| } | |
| std::copy(row.begin(), row.end(), state_out + state_offset); | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| // Decode одного токена: четыре независимых аккумулятора без внутреннего | |
| // динамического цикла по выходам, с тем же порядком сумм по группам. | |
| static void integer_single_int8(const Layer& l, const int8_t* x, | |
| const float* scales, float* y) { | |
| const int groups=(l.din+l.group-1)/l.group; | |
| int j=0; | |
| for (; j+4<=l.dout; j+=4) { | |
| const auto* w0=reinterpret_cast<const int8_t*>(l.codes)+j*l.din; | |
| const auto* w1=w0+l.din; | |
| const auto* w2=w1+l.din; | |
| const auto* w3=w2+l.din; | |
| float sums[4]={y[j],y[j+1],y[j+2],y[j+3]}; | |
| for (int g=0; g<groups; ++g) { | |
| int k=g*l.group, end=std::min(k+l.group,l.din); | |
| int32x4_t a0=vdupq_n_s32(0), a1=a0, a2=a0, a3=a0; | |
| for (; k+16<=end; k+=16) { | |
| const auto a=vld1q_s8(x+k); | |
| a0=vdotq_s32(a0,vld1q_s8(w0+k),a); | |
| a1=vdotq_s32(a1,vld1q_s8(w1+k),a); | |
| a2=vdotq_s32(a2,vld1q_s8(w2+k),a); | |
| a3=vdotq_s32(a3,vld1q_s8(w3+k),a); | |
| } | |
| int s0=vaddvq_s32(a0), s1=vaddvq_s32(a1); | |
| int s2=vaddvq_s32(a2), s3=vaddvq_s32(a3); | |
| for (; k<end; ++k) { | |
| s0+=w0[k]*x[k]; s1+=w1[k]*x[k]; | |
| s2+=w2[k]*x[k]; s3+=w3[k]*x[k]; | |
| } | |
| sums[0]+=s0*(scales[g]*l.scales[j*groups+g]); | |
| sums[1]+=s1*(scales[g]*l.scales[(j+1)*groups+g]); | |
| sums[2]+=s2*(scales[g]*l.scales[(j+2)*groups+g]); | |
| sums[3]+=s3*(scales[g]*l.scales[(j+3)*groups+g]); | |
| } | |
| y[j]=sums[0]; y[j+1]=sums[1]; y[j+2]=sums[2]; y[j+3]=sums[3]; | |
| } | |
| for (; j<l.dout; ++j) { | |
| const auto* w=reinterpret_cast<const int8_t*>(l.codes)+j*l.din; | |
| float sum_out=y[j]; | |
| for (int g=0; g<groups; ++g) { | |
| int k=g*l.group, end=std::min(k+l.group,l.din); | |
| auto a=vdupq_n_s32(0); | |
| for (; k+16<=end; k+=16) a=vdotq_s32(a,vld1q_s8(w+k),vld1q_s8(x+k)); | |
| int sum=vaddvq_s32(a); | |
| for (; k<end; ++k) sum+=w[k]*x[k]; | |
| sum_out+=sum*(scales[g]*l.scales[j*groups+g]); | |
| } | |
| y[j]=sum_out; | |
| } | |
| } | |
| // Четыре выхода используют общую загрузку активаций; пакет делит загрузки весов. | |
| template<int Rows> | |
| static void integer_base_quantized(const Layer& l, const int8_t* activations, | |
| const float* scales, float* y) { | |
| if constexpr (Rows==1) { | |
| if (l.mode==3) { | |
| integer_single_int8(l,activations,scales,y); | |
| return; | |
| } | |
| } | |
| const int groups=(l.din+l.group-1)/l.group; | |
| const int stride=l.mode==3 ? l.din : (l.din+1)/2; | |
| for (int j=0; j<l.dout; j+=4) { | |
| const int outputs=std::min(4,l.dout-j); | |
| for (int g=0; g<groups; ++g) { | |
| int k=g*l.group, end=std::min(k+l.group,l.din); | |
| int32x4_t accumulator[Rows][4]; | |
| int tails[Rows][4]={}; | |
| for (int row=0; row<Rows; ++row) | |
| for (int out=0; out<4; ++out) accumulator[row][out]=vdupq_n_s32(0); | |
| if (l.mode!=3 && (k&1)) { | |
| for (int out=0; out<outputs; ++out) { | |
| int q=int(l.codes[(j+out)*stride+k/2]>>4)-7; | |
| for (int row=0; row<Rows; ++row) tails[row][out]+=q*activations[row*l.din+k]; | |
| } | |
| ++k; | |
| } | |
| for (; k+16<=end; k+=16) { | |
| int8x16_t a[Rows]; | |
| for (int row=0; row<Rows; ++row) a[row]=vld1q_s8(activations+row*l.din+k); | |
| for (int out=0; out<outputs; ++out) { | |
| const uint8_t* weights=l.codes+(j+out)*stride; | |
| int8x16_t w; | |
| if (l.mode==3) w=vld1q_s8(reinterpret_cast<const int8_t*>(weights+k)); | |
| else { | |
| uint8x8_t packed=vld1_u8(weights+k/2); | |
| uint8x8x2_t q=vzip_u8(vand_u8(packed,vdup_n_u8(15)),vshr_n_u8(packed,4)); | |
| w=vsubq_s8(vreinterpretq_s8_u8(vcombine_u8(q.val[0],q.val[1])),vdupq_n_s8(7)); | |
| } | |
| for (int row=0; row<Rows; ++row) accumulator[row][out]=vdotq_s32(accumulator[row][out],w,a[row]); | |
| } | |
| } | |
| for (; k<end; ++k) | |
| for (int out=0; out<outputs; ++out) { | |
| const uint8_t* weights=l.codes+(j+out)*stride; | |
| int q=l.mode==3 ? reinterpret_cast<const int8_t*>(weights)[k] : int((weights[k/2]>>((k&1)*4))&15)-7; | |
| for (int row=0; row<Rows; ++row) tails[row][out]+=q*activations[row*l.din+k]; | |
| } | |
| for (int row=0; row<Rows; ++row) | |
| for (int out=0; out<outputs; ++out) | |
| y[row*l.dout+j+out]+=(vaddvq_s32(accumulator[row][out])+tails[row][out])* | |
| (scales[row*groups+g]*l.scales[(j+out)*groups+g]); | |
| } | |
| } | |
| } | |
| template<int Rows> | |
| static void integer_base(const Layer& l, const float* x, float* y) { | |
| const int groups=(l.din+l.group-1)/l.group; | |
| std::vector<int8_t> activations(Rows*l.din); | |
| std::vector<float> scales(Rows*groups); | |
| for (int row=0; row<Rows; ++row) | |
| for (int g=0; g<groups; ++g) { | |
| int start=g*l.group, end=std::min(start+l.group,l.din); | |
| float maximum=0; | |
| for (int k=start; k<end; ++k) maximum=std::max(maximum,std::abs(x[row*l.din+k])); | |
| float scale=maximum ? maximum/127 : 1.0f; | |
| scales[row*groups+g]=scale; | |
| for (int k=start; k<end; ++k) | |
| activations[row*l.din+k]=int8_t(std::max(-127.0f,std::min(127.0f,std::round(x[row*l.din+k]/scale)))); | |
| } | |
| integer_base_quantized<Rows>(l,activations.data(),scales.data(),y); | |
| } | |
| static void linear(const Layer& l, const float* x, float* y) { | |
| std::vector<float> normalized; | |
| const float* r = x; | |
| if (l.center) { | |
| normalized.resize(l.din); | |
| for (int i = 0; i < l.din; ++i) normalized[i] = (x[i] - l.center[i]) * l.unit[i]; | |
| r = normalized.data(); | |
| } | |
| std::copy(l.bias, l.bias + l.dout, y); | |
| if (l.mode >= 2) { | |
| integer_base<1>(l, r, y); | |
| } else if (l.mode == 0) { | |
| if (l.indices) { | |
| for (int j = 0; j < l.dout; ++j) | |
| for (int k = 0; k < l.width; ++k) | |
| y[j] += r[l.indices[j*l.width+k]] * l.weight[j*l.width+k]; | |
| } else cblas_sgemv(CblasRowMajor, CblasNoTrans, l.dout, l.din, 1.0f, | |
| l.weight, l.din, r, 1, 1.0f, y, 1); | |
| } else { | |
| int stride = (l.width + 1) / 2; | |
| int groups = (l.width + l.group - 1) / l.group; | |
| for (int j = 0; j < l.dout; ++j) { | |
| const uint8_t* row = l.codes + j * stride; | |
| for (int g = 0; g < groups; ++g) { | |
| float sum = 0; | |
| int end = std::min((g + 1) * l.group, l.width); | |
| int k = g * l.group; | |
| // SIMD packed decode and FMA, with scalar tails for odd groups. | |
| if (k & 1) { | |
| int q = (row[k/2] >> 4) - 7; | |
| int i = l.indices ? l.indices[j*l.width+k] : k; | |
| sum += r[i]*q; ++k; | |
| } | |
| float32x4_t acc = vdupq_n_f32(0); | |
| for (; k + 16 <= end; k += 16) { | |
| uint8x8_t packed = vld1_u8(row+k/2); | |
| uint8x8x2_t nibbles = vzip_u8(vand_u8(packed,vdup_n_u8(15)),vshr_n_u8(packed,4)); | |
| for (int h = 0; h < 2; ++h) { | |
| int16x8_t q = vmovl_s8(vsub_s8(vreinterpret_s8_u8(nibbles.val[h]),vdup_n_s8(7))); | |
| float32x4_t low, high; | |
| if (l.indices) { | |
| float values[8]; | |
| for (int z = 0; z < 8; ++z) | |
| values[z] = r[l.indices[j*l.width+k+h*8+z]]; | |
| low = vld1q_f32(values); | |
| high = vld1q_f32(values+4); | |
| } else { | |
| low = vld1q_f32(r+k+h*8); | |
| high = vld1q_f32(r+k+h*8+4); | |
| } | |
| acc = vfmaq_f32(acc,low,vcvtq_f32_s32(vmovl_s16(vget_low_s16(q)))); | |
| acc = vfmaq_f32(acc,high,vcvtq_f32_s32(vmovl_s16(vget_high_s16(q)))); | |
| } | |
| } | |
| sum += vaddvq_f32(acc); | |
| for (; k < end; ++k) { | |
| int q = ((row[k / 2] >> ((k & 1) * 4)) & 15) - 7; | |
| int i = l.indices ? l.indices[j * l.width + k] : k; | |
| sum += r[i] * q; | |
| } | |
| y[j] += sum * l.scales[j * groups + g]; | |
| } | |
| } | |
| } | |
| if (l.rank) { | |
| std::vector<float> h(l.rank); | |
| cblas_sgemv(CblasRowMajor, CblasNoTrans, l.rank, l.din, 1.0f, | |
| l.a, l.din, r, 1, 0.0f, h.data(), 1); | |
| cblas_sgemv(CblasRowMajor, CblasNoTrans, l.dout, l.rank, 1.0f, | |
| l.b, l.rank, h.data(), 1, 1.0f, y, 1); | |
| } | |
| } | |
| extern "C" int relative_lookup_rows(const Layer* layer, const int32_t* rows, | |
| int count, float* output) { | |
| if (!layer || count < 0 || (count && (!rows || !output))) return 1; | |
| const auto& l = *layer; | |
| if (l.din < 1 || l.dout < 1 || l.group < 1 || l.rank || !zero_center(l) || | |
| l.indices || (l.mode != 1 && l.mode != 2 && l.mode != 3)) return 2; | |
| if (!l.codes || !l.scales) return 3; | |
| const int groups = (l.din + l.group - 1) / l.group; | |
| const int stride = l.mode == 3 ? l.din : (l.din + 1) / 2; | |
| for (int n = 0; n < count; ++n) { | |
| const int j = rows[n]; | |
| if (j < 0 || j >= l.dout) return 4; | |
| for (int k = 0; k < l.din; ++k) { | |
| float q; | |
| if (l.mode == 3) { | |
| q = reinterpret_cast<const int8_t*>(l.codes + j*stride)[k]; | |
| } else { | |
| q = ((l.codes[j*stride+k/2] >> ((k&1)*4)) & 15) - 7; | |
| } | |
| const float unit = l.unit ? l.unit[k] : 1.0f; | |
| output[n*l.din+k] = q*l.scales[j*groups+k/l.group]*unit; | |
| } | |
| } | |
| return 0; | |
| } | |
| extern "C" int relative_forward_many(const Layer* layers, int count, | |
| const float* input, float** outputs, | |
| int rows) { | |
| if (!layers || count < 1 || rows < 0 || (rows && (!input || !outputs))) return 1; | |
| if (!relative_integer_dot_supported()) return 5; | |
| const auto& first = layers[0]; | |
| if (first.din < 1 || first.group < 1 || first.rank || !zero_center(first) || | |
| (first.mode != 2 && first.mode != 3)) return 2; | |
| const int groups = (first.din + first.group - 1) / first.group; | |
| for (int k = 0; k < count; ++k) { | |
| const auto& l = layers[k]; | |
| if (l.din != first.din || l.group != first.group || l.dout < 1 || | |
| l.rank || !zero_center(l) || l.indices || !l.bias || !l.codes || !l.scales || | |
| (l.mode != 2 && l.mode != 3) || | |
| std::min(l.group,l.din) > INT32_MAX/(127*127) || (rows && !outputs[k])) return 3; | |
| for (int i = 0; i < first.din; ++i) { | |
| const float a = first.unit ? first.unit[i] : 1.0f; | |
| const float b = l.unit ? l.unit[i] : 1.0f; | |
| if (a != b) return 4; | |
| } | |
| } | |
| std::vector<int8_t> activations(rows*first.din); | |
| std::vector<float> activation_scales(rows*groups); | |
| for (int row = 0; row < rows; ++row) { | |
| for (int g = 0; g < groups; ++g) { | |
| const int start = g*first.group, end = std::min(start+first.group, first.din); | |
| float maximum = 0.0f; | |
| for (int i = start; i < end; ++i) { | |
| const float unit = first.unit ? first.unit[i] : 1.0f; | |
| maximum = std::max(maximum, std::abs(input[row*first.din+i]*unit)); | |
| } | |
| const float scale = maximum ? maximum/127.0f : 1.0f; | |
| activation_scales[row*groups+g] = scale; | |
| for (int i = start; i < end; ++i) { | |
| const float unit = first.unit ? first.unit[i] : 1.0f; | |
| activations[row*first.din+i] = int8_t(std::max(-127.0f, std::min(127.0f, | |
| std::round(input[row*first.din+i]*unit/scale)))); | |
| } | |
| } | |
| } | |
| for (int n = 0; n < count; ++n) { | |
| const auto& l = layers[n]; | |
| for (int row = 0; row < rows; ++row) | |
| std::copy(l.bias, l.bias+l.dout, outputs[n]+row*l.dout); | |
| int row = 0; | |
| for (; row+4 <= rows; row += 4) | |
| integer_base_quantized<4>(l,activations.data()+row*l.din, | |
| activation_scales.data()+row*groups,outputs[n]+row*l.dout); | |
| for (; row < rows; ++row) | |
| integer_base_quantized<1>(l,activations.data()+row*l.din, | |
| activation_scales.data()+row*groups,outputs[n]+row*l.dout); | |
| } | |
| return 0; | |
| } | |
| extern "C" int relative_forward(const Layer* layers, int count, const float* input, | |
| float* output, int rows) { | |
| if (!layers || count < 1 || rows < 0) return 1; | |
| for (int k = 0; k < count; ++k) { | |
| const auto& l = layers[k]; | |
| if (l.din < 1 || l.dout < 1 || l.width < 1 || l.group < 1 || | |
| (k && layers[k-1].dout != l.din)) return 2; | |
| if (l.mode>=2 && (!relative_integer_dot_supported() || l.indices)) return 3; | |
| } | |
| // Prefill uses the same optimized dense BLAS path as ordinary FP32 models. | |
| if (count == 1 && rows > 1 && !layers[0].mode && !layers[0].indices && | |
| !layers[0].center && !layers[0].rank) { | |
| const auto& l = layers[0]; | |
| for (int row = 0; row < rows; ++row) | |
| std::copy(l.bias, l.bias+l.dout, output+row*l.dout); | |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, rows, l.dout, | |
| l.din, 1.0f, input, l.din, l.weight, l.din, | |
| 1.0f, output, l.dout); | |
| return 0; | |
| } | |
| if (count==1 && rows>1 && layers[0].mode>=2) { | |
| const auto& l=layers[0]; | |
| std::vector<float> normalized; | |
| const float* x=input; | |
| if (l.center) { | |
| normalized.resize(rows*l.din); | |
| for (int row=0; row<rows; ++row) | |
| for (int k=0; k<l.din; ++k) | |
| normalized[row*l.din+k]=(input[row*l.din+k]-l.center[k])*l.unit[k]; | |
| x=normalized.data(); | |
| } | |
| for (int row=0; row<rows; ++row) std::copy(l.bias,l.bias+l.dout,output+row*l.dout); | |
| int row=0; | |
| for (; row+4<=rows; row+=4) integer_base<4>(l,x+row*l.din,output+row*l.dout); | |
| for (; row<rows; ++row) integer_base<1>(l,x+row*l.din,output+row*l.dout); | |
| if (l.rank) { | |
| std::vector<float> correction(rows*l.rank); | |
| cblas_sgemm(CblasRowMajor,CblasNoTrans,CblasTrans,rows,l.rank,l.din,1.0f,x,l.din,l.a,l.din,0.0f,correction.data(),l.rank); | |
| cblas_sgemm(CblasRowMajor,CblasNoTrans,CblasTrans,rows,l.dout,l.rank,1.0f,correction.data(),l.rank,l.b,l.rank,1.0f,output,l.dout); | |
| } | |
| return 0; | |
| } | |
| if (count == 1 && rows > 1 && layers[0].mode==1 && !layers[0].indices) { | |
| const auto& l = layers[0]; | |
| std::vector<float> normalized; | |
| const float* x = input; | |
| if (l.center) { | |
| normalized.resize(rows*l.din); | |
| for (int row = 0; row < rows; ++row) | |
| for (int k = 0; k < l.din; ++k) | |
| normalized[row*l.din+k] = (input[row*l.din+k]-l.center[k])*l.unit[k]; | |
| x = normalized.data(); | |
| } | |
| for (int row = 0; row < rows; ++row) | |
| std::copy(l.bias, l.bias+l.dout, output+row*l.dout); | |
| // Only a bounded input tile is decoded, never the full weight matrix. | |
| const int tile_width = std::min(32, l.din); | |
| std::vector<float> tile(tile_width*l.dout); | |
| const int stride = (l.din+1)/2; | |
| const int groups = (l.din+l.group-1)/l.group; | |
| for (int start = 0; start < l.din; start += tile_width) { | |
| int width = std::min(tile_width, l.din-start); | |
| for (int j = 0; j < l.dout; ++j) | |
| for (int k = 0; k < width; ++k) { | |
| int ix = start+k; | |
| int q = ((l.codes[j*stride+ix/2] >> ((ix&1)*4))&15)-7; | |
| tile[j*width+k] = q*l.scales[j*groups+ix/l.group]; | |
| } | |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, rows, l.dout, | |
| width, 1.0f, x+start, l.din, tile.data(), width, | |
| 1.0f, output, l.dout); | |
| } | |
| if (l.rank) { | |
| std::vector<float> correction(rows*l.rank); | |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, rows, l.rank, | |
| l.din, 1.0f, x, l.din, l.a, l.din, | |
| 0.0f, correction.data(), l.rank); | |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, rows, l.dout, | |
| l.rank, 1.0f, correction.data(), l.rank, l.b, l.rank, | |
| 1.0f, output, l.dout); | |
| } | |
| return 0; | |
| } | |
| for (int row = 0; row < rows; ++row) { | |
| const float* x = input + row * layers[0].din; | |
| std::vector<float> current, next; | |
| for (int k = 0; k < count; ++k) { | |
| next.resize(layers[k].dout); | |
| linear(layers[k], x, next.data()); | |
| if (k + 1 < count) for (float& v : next) v = std::max(0.0f, v); | |
| current.swap(next); | |
| x = current.data(); | |
| } | |
| std::copy(current.begin(), current.end(), output + row * layers[count-1].dout); | |
| } | |
| return 0; | |
| } | |