our_knp/altai-objects/bench_conv.cpp
2026-10-15 08:11:15 +03:00

92 lines
4.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Speed check: a convolution-like feature layer built from AltAI neurons in KNP.
// usage: bench_conv <K> <stride> <images> <backend.so>
// K prototypes × positions (6×6×3 patches of a 32×32×3 image with the given stride) AltAI neurons; random integer
// weights; random input spikes (pixel rate ~0.4 per active step); 15 steps per image, 10 of them active.
#include <knp/framework/model_executor.h>
#include <knp/framework/network.h>
#include <knp/neuron-traits/altai_lif.h>
#include <knp/synapse-traits/delta.h>
#include <chrono>
#include <iostream>
#include <random>
#include <string>
using DeltaProjection = knp::core::Projection<knp::synapse_traits::DeltaSynapse>;
using AltAIPopulation = knp::core::Population<knp::neuron_traits::AltAILIF>;
constexpr int side = 32, patch = 6, channels = 3, steps_per_image = 15, active_steps = 10;
int main(int argc, const char *const argv[])
{
const int k = std::stoi(argv[1]), stride = std::stoi(argv[2]), images = std::stoi(argv[3]);
const int per_side = (side - patch) / stride + 1, positions = per_side * per_side;
const size_t neurons = static_cast<size_t>(k) * positions, fan_in = patch * patch * channels;
AltAIPopulation population(
[](size_t)
{
knp::neuron_traits::neuron_parameters<knp::neuron_traits::AltAILIF> p;
p.activation_threshold_ = 1000;
p.negative_activation_threshold_ = 0;
p.potential_reset_value_ = 0;
return p;
},
neurons);
std::mt19937 rng(1);
std::uniform_int_distribution<int> weight(-60, 60);
std::vector<int> w(fan_in * k);
for (auto &v : w) v = weight(rng);
// Synapse index → (neuron, patch element); the source is the input channel index of that pixel.
DeltaProjection input_projection(
knp::core::UID{false}, population.get_uid(),
[&](size_t index) -> std::optional<DeltaProjection::Synapse>
{
const size_t neuron = index / fan_in, e = index % fan_in;
const int proto = static_cast<int>(neuron / positions), pos = static_cast<int>(neuron % positions);
const int py = (pos / per_side) * stride, px = (pos % per_side) * stride;
const int c = static_cast<int>(e / (patch * patch)), dy = static_cast<int>(e % (patch * patch)) / patch,
dx = static_cast<int>(e % patch);
const size_t source = static_cast<size_t>(c * side * side + (py + dy) * side + px + dx);
return DeltaProjection::Synapse{
{static_cast<float>(w[proto * fan_in + e]), 1, knp::synapse_traits::OutputType::EXCITATORY}, source,
neuron};
},
neurons * fan_in);
const knp::core::UID input_uid = input_projection.get_uid(), output_uid = population.get_uid();
knp::framework::Network network;
network.add_population(std::move(population));
network.add_projection(std::move(input_projection));
knp::core::UID in_channel, out_channel;
knp::framework::Model model(std::move(network));
model.add_input_channel(in_channel, input_uid);
model.add_output_channel(out_channel, output_uid);
std::bernoulli_distribution fire(0.4);
auto input_gen = [&](knp::core::Step step) -> knp::core::messaging::SpikeData
{
knp::core::messaging::SpikeData s;
if (step % steps_per_image >= active_steps) return s;
for (size_t i = 0; i < side * side * channels; ++i)
if (fire(rng)) s.push_back(i);
return s;
};
knp::framework::BackendLoader loader;
knp::framework::ModelExecutor executor(model, loader.load(argv[4]), {{in_channel, input_gen}});
auto &out = executor.get_loader().get_output_channel(out_channel);
const auto t0 = std::chrono::steady_clock::now();
executor.start([&](size_t step) { return step < static_cast<size_t>(images * steps_per_image); });
const double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
size_t spikes = 0;
for (const auto &m : out.update()) spikes += m.neuron_indexes_.size();
std::cout << "K=" << k << " stride=" << stride << " neurons=" << neurons << " synapses=" << neurons * fan_in
<< " images=" << images << " seconds=" << sec << " per_image=" << sec / images
<< " output_spikes_per_image=" << spikes / images << std::endl;
}