File size: 5,245 Bytes
9422383
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "arm_yolo26n.h"

#include <pthreadpool.h>
#include <xnnpack.h>

#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>

#include "buffer_validation.h"
#include "fixed_graph.h"
#include "fixed_schedule.h"
#include "model_store.h"

namespace {

// Owns the complete lifetime of one runtime instance. Members are declared in
// dependency order; explicit teardown releases graph runtimes before their
// shared workspace, threadpool, scratch buffers, and model storage disappear.
struct NativeRuntime {
  NativeRuntime() = default;
  NativeRuntime(const NativeRuntime&) = delete;
  NativeRuntime& operator=(const NativeRuntime&) = delete;
  ~NativeRuntime() {
    for (xnn_runtime_t runtime : runtimes) {
      if (runtime) xnn_delete_runtime(runtime);
    }
    if (workspace) xnn_release_workspace(workspace);
    if (threadpool) pthreadpool_destroy(threadpool);
    std::free(arena);
    std::free(project);
  }

  ModelStore model;
  ScheduleState schedule;
  std::array<xnn_runtime_t, kGraphCount> runtimes{};
  xnn_workspace_t workspace = nullptr;
  pthreadpool_t threadpool = nullptr;
  uint8_t* arena = nullptr;
  uint16_t* project = nullptr;
  std::atomic<uint64_t> calls{0};
};

bool Allocate(void** output, size_t bytes) {
  return bytes != 0 && posix_memalign(output, yolo26::kTensorAlignment, bytes) == 0;
}

bool Initialize(NativeRuntime* native, const void* model_data, size_t model_size) {
  // Validate model-derived dimensions before creating any native execution
  // resources. The destructor handles every partially initialized state.
  if (!native->model.Load(model_data, model_size) ||
      !InitializeSchedule(native->model.resolution(), native->model.class_count(),
                          &native->schedule) ||
      xnn_initialize(nullptr) != xnn_status_success ||
      xnn_create_workspace(&native->workspace) != xnn_status_success) {
    return false;
  }
  native->threadpool = pthreadpool_create(yolo26::kThreadCount);
  if (!native->threadpool ||
      pthreadpool_get_threads_count(native->threadpool) != yolo26::kThreadCount ||
      !Allocate(reinterpret_cast<void**>(&native->arena), native->schedule.arena_bytes) ||
      !Allocate(reinterpret_cast<void**>(&native->project), native->schedule.project_bytes)) {
    return false;
  }
  const GraphContext graphs{
      &native->model,
      native->workspace,
      native->threadpool,
      &native->runtimes,
  };
  return BuildFixedGraphs(graphs) && ConfigureFixedProjection(native->runtimes);
}

}  // namespace

extern "C" y26_native_runtime* y26_native_create(const void* model_data, size_t model_size) {
  if (!model_data) return nullptr;
  auto runtime = std::make_unique<NativeRuntime>();
  if (!Initialize(runtime.get(), model_data, model_size)) return nullptr;
  return reinterpret_cast<y26_native_runtime*>(runtime.release());
}

extern "C" y26_native_status y26_native_execute(y26_native_runtime* runtime, const float* input,
                                                size_t input_bytes, float* output,
                                                size_t output_bytes) {
  auto* native = reinterpret_cast<NativeRuntime*>(runtime);
  if (!native || !input || !output || input_bytes != native->schedule.input_bytes ||
      output_bytes != native->schedule.output_bytes ||
      yolo26::BuffersOverlap(input, input_bytes, output, output_bytes)) {
    return Y26_NATIVE_INVALID_ARGUMENT;
  }
  if (!RunFixedSchedule(native->schedule, native->runtimes, native->threadpool, native->arena,
                        native->project, input, output)) {
    return Y26_NATIVE_EXECUTION_FAILED;
  }
  if (!OutputIsFinite(native->schedule, output)) return Y26_NATIVE_NONFINITE_OUTPUT;
  ++native->calls;
  return Y26_NATIVE_OK;
}

extern "C" void y26_native_destroy(y26_native_runtime* runtime) {
  delete reinterpret_cast<NativeRuntime*>(runtime);
}

extern "C" uint64_t y26_native_calls(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->calls.load() : 0;
}

extern "C" uint32_t y26_native_graphs(const y26_native_runtime* runtime) {
  return runtime ? kGraphCount : 0;
}

extern "C" uint32_t y26_native_resolution(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->model.resolution() : 0;
}

extern "C" uint32_t y26_native_class_count(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->model.class_count() : 0;
}

extern "C" size_t y26_native_input_bytes(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->schedule.input_bytes : 0;
}

extern "C" size_t y26_native_output_bytes(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->schedule.output_bytes : 0;
}

extern "C" size_t y26_native_arena_bytes(const y26_native_runtime* runtime) {
  const auto* native = reinterpret_cast<const NativeRuntime*>(runtime);
  return native ? native->schedule.arena_bytes : 0;
}