Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/mel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ MelKernel::MelKernel(const ModelLoader& ml) {
pk::weight_to_host_f32(ml, "preprocessor.featurizer.fb", fbuf);
const float* fd = fbuf.data();
std::memcpy(fb_.data(), fd, sizeof(float) * (size_t)n_mels_ * n_bins_);
// Build the sparse (bin, weight) index: skip exact-zero weights. This is
// bit-identical to the dense accumulation (0.0*power == 0.0 for finite
// power) while removing ~98% of the per-frame filterbank MACs.
fb_nz_off_.assign((size_t)n_mels_ + 1, 0);
for (int m = 0; m < n_mels_; ++m) {
const float* fbm = &fb_[(size_t)m * n_bins_];
for (int b = 0; b < n_bins_; ++b)
if (fbm[b] != 0.0f) { fb_nz_idx_.push_back(b); fb_nz_val_.push_back(fbm[b]); }
fb_nz_off_[m + 1] = (int32_t)fb_nz_idx_.size();
}
}
}

Expand All @@ -81,11 +91,15 @@ void MelKernel::frame_logmel(const double* frame_in, float* out_col, int out_str
}

// ----- mel projection + log(mel + guard) -----
// Iterate only the nonzero filterbank weights (fb is ~98% zeros); bit-identical
// to the dense sum since an exact-zero weight contributes exactly 0.0.
const int32_t* off = fb_nz_off_.data();
const int32_t* idx = fb_nz_idx_.data();
const float* val = fb_nz_val_.data();
for (int m = 0; m < n_mels_; ++m) {
const float* fbm = &fb_[(size_t)m * n_bins_];
double acc = 0.0;
for (int b = 0; b < n_bins_; ++b)
acc += (double)fbm[b] * power[b];
for (int32_t k = off[m]; k < off[m + 1]; ++k)
acc += (double)val[k] * power[idx[k]];
out_col[(size_t)m * out_stride] = (float)std::log(acc + (double)log_guard_);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/mel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ struct MelKernel {
bool per_feature_;
std::vector<float> window_; // [n_fft] (centered-padded Hann from GGUF)
std::vector<float> fb_; // [n_mels, n_bins] row-major (fb_[m*n_bins + b])

// Sparse filterbank: the mel matrix is overwhelmingly zeros (each filter is
// band-limited in frequency). Store only the nonzero (bin, weight) pairs so
// the per-frame mel projection skips the ~98% zero entries. Bit-identical to
// the dense accumulation (skipping an exact-zero weight contributes 0.0).
// Flat arrays + per-mel offset/count (no per-frame allocation).
std::vector<int32_t> fb_nz_idx_; // bin index per nonzero entry
std::vector<float> fb_nz_val_; // weight per nonzero entry
std::vector<int32_t> fb_nz_off_; // [n_mels+1] prefix offsets into the above
};

class MelFrontend {
Expand Down
Loading