@delali/narsil-react-native 0.0.0-stage → 0.3.1-next.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NarsilReactNative.podspec +57 -0
- package/README.md +46 -2
- package/android/CMakeLists.txt +48 -0
- package/android/build.gradle +151 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/AndroidEnvironment.cpp +44 -0
- package/android/src/main/cpp/AndroidEnvironment.hpp +11 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilAndroidEnvironment.kt +29 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilReactNativePackage.kt +22 -0
- package/app.plugin.js +25 -0
- package/cpp/AttachedField.cpp +234 -0
- package/cpp/AttachedField.hpp +61 -0
- package/cpp/Compression.cpp +113 -0
- package/cpp/Compression.hpp +16 -0
- package/cpp/GraphHeldAlone.cpp +29 -0
- package/cpp/GraphHeldAlone.hpp +25 -0
- package/cpp/HostMemory.cpp +123 -0
- package/cpp/HostMemory.hpp +53 -0
- package/cpp/HybridNarsilCoreImpl.cpp +390 -0
- package/cpp/HybridNarsilCoreImpl.hpp +68 -0
- package/cpp/HybridNarsilFilesImpl.cpp +245 -0
- package/cpp/HybridNarsilFilesImpl.hpp +34 -0
- package/cpp/HybridNarsilHostImpl.cpp +126 -0
- package/cpp/HybridNarsilHostImpl.hpp +42 -0
- package/cpp/NativeAtomics.cpp +110 -0
- package/cpp/NativeAtomics.hpp +11 -0
- package/cpp/ProcessInfo.cpp +83 -0
- package/cpp/ProcessInfo.hpp +11 -0
- package/cpp/ThreadPriority.cpp +77 -0
- package/cpp/ThreadPriority.hpp +8 -0
- package/cpp/WorkletRuntimeDispatcher.cpp +61 -0
- package/cpp/WorkletRuntimeDispatcher.hpp +9 -0
- package/cpp/core/include/narsil_core.h +208 -0
- package/cpp/core/src/connect.c +213 -0
- package/cpp/core/src/distance.c +103 -0
- package/cpp/core/src/files.c +99 -0
- package/cpp/core/src/heap.c +84 -0
- package/cpp/core/src/internal.h +284 -0
- package/cpp/core/src/kernels_code.c +209 -0
- package/cpp/core/src/kernels_float.c +197 -0
- package/cpp/core/src/kernels_pair.c +133 -0
- package/cpp/core/src/last_links.c +145 -0
- package/cpp/core/src/links.c +195 -0
- package/cpp/core/src/locks.c +168 -0
- package/cpp/core/src/nearest_links.c +147 -0
- package/cpp/core/src/place.c +215 -0
- package/cpp/core/src/quantize.c +226 -0
- package/cpp/core/src/records.c +97 -0
- package/cpp/core/src/remove.c +205 -0
- package/cpp/core/src/vectors.c +139 -0
- package/cpp/core/src/walk.c +231 -0
- package/cpp/core/src/workspace.c +167 -0
- package/ios/PrivacyInfo.xcprivacy +23 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +41 -0
- package/lib/constants.d.ts +9 -0
- package/lib/constants.js +9 -0
- package/lib/engine/byte-files.d.ts +5 -0
- package/lib/engine/byte-files.js +97 -0
- package/lib/engine/core.d.ts +3 -0
- package/lib/engine/core.js +113 -0
- package/lib/engine/files.d.ts +3 -0
- package/lib/engine/files.js +188 -0
- package/lib/engine/host.d.ts +13 -0
- package/lib/engine/host.js +102 -0
- package/lib/engine/install.d.ts +4 -0
- package/lib/engine/install.js +29 -0
- package/lib/engine/native-error.d.ts +1 -0
- package/lib/engine/native-error.js +12 -0
- package/lib/engine/stand-ins/atomics.d.ts +5 -0
- package/lib/engine/stand-ins/atomics.js +236 -0
- package/lib/engine/stand-ins/crypto.d.ts +2 -0
- package/lib/engine/stand-ins/crypto.js +65 -0
- package/lib/engine/stand-ins/process.d.ts +2 -0
- package/lib/engine/stand-ins/process.js +22 -0
- package/lib/engine/stand-ins/streams.d.ts +1 -0
- package/lib/engine/stand-ins/streams.js +31 -0
- package/lib/engine/stand-ins/structured-clone.d.ts +1 -0
- package/lib/engine/stand-ins/structured-clone.js +121 -0
- package/lib/engine/streamed.d.ts +6 -0
- package/lib/engine/streamed.js +29 -0
- package/lib/engine/transfer.d.ts +3 -0
- package/lib/engine/transfer.js +77 -0
- package/lib/errors.d.ts +25 -0
- package/lib/errors.js +59 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/methods.d.ts +5 -0
- package/lib/methods.js +47 -0
- package/lib/native-objects.d.ts +2 -0
- package/lib/native-objects.js +24 -0
- package/lib/specs/core.nitro.d.ts +79 -0
- package/lib/specs/core.nitro.js +1 -0
- package/lib/specs/files.nitro.d.ts +36 -0
- package/lib/specs/files.nitro.js +1 -0
- package/lib/specs/host.nitro.d.ts +24 -0
- package/lib/specs/host.nitro.js +1 -0
- package/lib/transferred.d.ts +15 -0
- package/lib/transferred.js +6 -0
- package/lib/types.d.ts +136 -0
- package/lib/types.js +1 -0
- package/lib/worklets/app-turns.d.ts +1 -0
- package/lib/worklets/app-turns.js +44 -0
- package/lib/worklets/client.d.ts +30 -0
- package/lib/worklets/client.js +144 -0
- package/lib/worklets/index.d.ts +4 -0
- package/lib/worklets/index.js +2 -0
- package/lib/worklets/instances.d.ts +15 -0
- package/lib/worklets/instances.js +194 -0
- package/lib/worklets/json-part.d.ts +1 -0
- package/lib/worklets/json-part.js +42 -0
- package/lib/worklets/runtime.d.ts +25 -0
- package/lib/worklets/runtime.js +80 -0
- package/lib/worklets/transfer-queue.d.ts +7 -0
- package/lib/worklets/transfer-queue.js +79 -0
- package/lib/worklets/types.d.ts +26 -0
- package/lib/worklets/types.js +1 -0
- package/nitro.json +32 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.cmake +85 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.cpp +69 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/narsil/NarsilReactNativeOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NarsilReactNative+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.mm +55 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/DirectoryEntry.hpp +91 -0
- package/nitrogen/generated/shared/c++/FileStatus.hpp +103 -0
- package/nitrogen/generated/shared/c++/GraphMemory.hpp +120 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.cpp +35 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.hpp +100 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.cpp +37 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.hpp +86 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.cpp +34 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.hpp +78 -0
- package/nitrogen/generated/shared/c++/MemoryView.hpp +91 -0
- package/nitrogen/generated/shared/c++/NativeConnectOutcome.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeScores.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeSearchHits.hpp +91 -0
- package/nitrogen/generated/shared/c++/ProcessMemory.hpp +87 -0
- package/nitrogen/generated/shared/c++/StoreMemory.hpp +142 -0
- package/package.json +87 -3
- package/react-native.config.js +8 -0
- package/src/client.ts +44 -0
- package/src/constants.ts +9 -0
- package/src/engine/byte-files.ts +99 -0
- package/src/engine/core.ts +162 -0
- package/src/engine/files.ts +186 -0
- package/src/engine/host.ts +111 -0
- package/src/engine/install.ts +31 -0
- package/src/engine/native-error.ts +10 -0
- package/src/engine/stand-ins/atomics.ts +288 -0
- package/src/engine/stand-ins/crypto.ts +81 -0
- package/src/engine/stand-ins/process.ts +30 -0
- package/src/engine/stand-ins/streams.ts +45 -0
- package/src/engine/stand-ins/structured-clone.ts +118 -0
- package/src/engine/streamed.ts +40 -0
- package/src/engine/transfer.ts +72 -0
- package/src/errors.ts +73 -0
- package/src/index.ts +3 -0
- package/src/methods.ts +52 -0
- package/src/native-objects.ts +30 -0
- package/src/specs/core.nitro.ts +140 -0
- package/src/specs/files.nitro.ts +42 -0
- package/src/specs/host.nitro.ts +30 -0
- package/src/transferred.ts +18 -0
- package/src/types.ts +164 -0
- package/src/worklets/app-turns.ts +48 -0
- package/src/worklets/client.ts +165 -0
- package/src/worklets/index.ts +4 -0
- package/src/worklets/instances.ts +231 -0
- package/src/worklets/json-part.ts +38 -0
- package/src/worklets/runtime.ts +190 -0
- package/src/worklets/transfer-queue.ts +98 -0
- package/src/worklets/types.ts +37 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
|
|
7
|
+
#define LANE_WIDTH 4U
|
|
8
|
+
#define UNROLLED_LANES 4U
|
|
9
|
+
#define UNROLLED_WIDTH (LANE_WIDTH * UNROLLED_LANES)
|
|
10
|
+
|
|
11
|
+
#ifdef NARSIL_NEON
|
|
12
|
+
#include <arm_neon.h>
|
|
13
|
+
typedef float32x4_t lanes;
|
|
14
|
+
static inline lanes lanes_zero(void) { return vdupq_n_f32(0); }
|
|
15
|
+
static inline lanes lanes_load(const float *source) { return vld1q_f32(source); }
|
|
16
|
+
static inline lanes lanes_add(lanes lhs, lanes rhs) { return vaddq_f32(lhs, rhs); }
|
|
17
|
+
static inline lanes lanes_sub(lanes lhs, lanes rhs) { return vsubq_f32(lhs, rhs); }
|
|
18
|
+
static inline lanes lanes_mul(lanes lhs, lanes rhs) { return vmulq_f32(lhs, rhs); }
|
|
19
|
+
static inline float lanes_sum_in_pairs(lanes values) {
|
|
20
|
+
return (vgetq_lane_f32(values, 0) + vgetq_lane_f32(values, 1)) +
|
|
21
|
+
(vgetq_lane_f32(values, 2) + vgetq_lane_f32(values, 3));
|
|
22
|
+
}
|
|
23
|
+
#elif defined(NARSIL_SSE2)
|
|
24
|
+
#include <emmintrin.h>
|
|
25
|
+
#if (defined(__GNUC__) || defined(__clang__)) && !defined(NARSIL_SSE2_ONLY)
|
|
26
|
+
#define NARSIL_AVX2_DOT 1
|
|
27
|
+
#include <cpuid.h>
|
|
28
|
+
#include <immintrin.h>
|
|
29
|
+
#include <stdatomic.h>
|
|
30
|
+
#endif
|
|
31
|
+
typedef __m128 lanes;
|
|
32
|
+
static inline lanes lanes_zero(void) { return _mm_setzero_ps(); }
|
|
33
|
+
static inline lanes lanes_load(const float *source) { return _mm_loadu_ps(source); }
|
|
34
|
+
static inline lanes lanes_add(lanes lhs, lanes rhs) { return _mm_add_ps(lhs, rhs); }
|
|
35
|
+
static inline lanes lanes_sub(lanes lhs, lanes rhs) { return _mm_sub_ps(lhs, rhs); }
|
|
36
|
+
static inline lanes lanes_mul(lanes lhs, lanes rhs) { return _mm_mul_ps(lhs, rhs); }
|
|
37
|
+
static inline float lanes_sum_in_pairs(lanes values) {
|
|
38
|
+
float lane[LANE_WIDTH];
|
|
39
|
+
_mm_storeu_ps(lane, values);
|
|
40
|
+
return (lane[0] + lane[1]) + (lane[2] + lane[3]);
|
|
41
|
+
}
|
|
42
|
+
#else
|
|
43
|
+
typedef struct {
|
|
44
|
+
float lane[LANE_WIDTH];
|
|
45
|
+
} lanes;
|
|
46
|
+
static inline lanes lanes_zero(void) { return (lanes){{0, 0, 0, 0}}; }
|
|
47
|
+
static inline lanes lanes_load(const float *source) { return (lanes){{source[0], source[1], source[2], source[3]}}; }
|
|
48
|
+
static inline lanes lanes_add(lanes lhs, lanes rhs) {
|
|
49
|
+
return (lanes){
|
|
50
|
+
{lhs.lane[0] + rhs.lane[0], lhs.lane[1] + rhs.lane[1], lhs.lane[2] + rhs.lane[2], lhs.lane[3] + rhs.lane[3]}};
|
|
51
|
+
}
|
|
52
|
+
static inline lanes lanes_sub(lanes lhs, lanes rhs) {
|
|
53
|
+
return (lanes){
|
|
54
|
+
{lhs.lane[0] - rhs.lane[0], lhs.lane[1] - rhs.lane[1], lhs.lane[2] - rhs.lane[2], lhs.lane[3] - rhs.lane[3]}};
|
|
55
|
+
}
|
|
56
|
+
static inline lanes lanes_mul(lanes lhs, lanes rhs) {
|
|
57
|
+
return (lanes){
|
|
58
|
+
{lhs.lane[0] * rhs.lane[0], lhs.lane[1] * rhs.lane[1], lhs.lane[2] * rhs.lane[2], lhs.lane[3] * rhs.lane[3]}};
|
|
59
|
+
}
|
|
60
|
+
static inline float lanes_sum_in_pairs(lanes values) {
|
|
61
|
+
return (values.lane[0] + values.lane[1]) + (values.lane[2] + values.lane[3]);
|
|
62
|
+
}
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
static inline uint32_t whole_steps(uint32_t count, uint32_t width) { return count - (count % width); }
|
|
66
|
+
|
|
67
|
+
static inline lanes lane_product(const float *lhs, const float *rhs, size_t lane) {
|
|
68
|
+
return lanes_mul(lanes_load(lhs + (lane * LANE_WIDTH)), lanes_load(rhs + (lane * LANE_WIDTH)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
typedef struct {
|
|
72
|
+
lanes sums[UNROLLED_LANES];
|
|
73
|
+
uint32_t offset;
|
|
74
|
+
} unrolled_dot;
|
|
75
|
+
|
|
76
|
+
static inline float finish_dot(const float *lhs, const float *rhs, uint32_t dimension, unrolled_dot dot) {
|
|
77
|
+
uint32_t lanes_end = whole_steps(dimension, LANE_WIDTH);
|
|
78
|
+
uint32_t offset = dot.offset;
|
|
79
|
+
lanes first = dot.sums[0];
|
|
80
|
+
for (; offset < lanes_end; offset += LANE_WIDTH) {
|
|
81
|
+
first = lanes_add(first, lane_product(lhs + offset, rhs + offset, 0));
|
|
82
|
+
}
|
|
83
|
+
float sum = lanes_sum_in_pairs(lanes_add(lanes_add(first, dot.sums[1]), lanes_add(dot.sums[2], dot.sums[3])));
|
|
84
|
+
for (; offset < dimension; offset++) { sum = sum + (lhs[offset] * rhs[offset]); }
|
|
85
|
+
return sum;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#ifdef NARSIL_AVX2_DOT
|
|
89
|
+
#define CPUID_FEATURES_LEAF 1U
|
|
90
|
+
#define CPUID_EXTENDED_FEATURES_LEAF 7U
|
|
91
|
+
#define XCR0_SSE_AND_AVX_STATE 6U
|
|
92
|
+
#define WIDE_LANE_WIDTH 8U
|
|
93
|
+
#define AVX2_UNKNOWN 0
|
|
94
|
+
#define AVX2_ABSENT 1
|
|
95
|
+
#define AVX2_PRESENT 2
|
|
96
|
+
|
|
97
|
+
static atomic_int avx2_state = AVX2_UNKNOWN;
|
|
98
|
+
|
|
99
|
+
static uint32_t enabled_register_state(void) {
|
|
100
|
+
uint32_t low = 0;
|
|
101
|
+
uint32_t high = 0;
|
|
102
|
+
__asm__ volatile("xgetbv" : "=a"(low), "=d"(high) : "c"(0));
|
|
103
|
+
(void)high;
|
|
104
|
+
return low;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static int processor_runs_avx2(void) {
|
|
108
|
+
unsigned int eax = 0;
|
|
109
|
+
unsigned int ebx = 0;
|
|
110
|
+
unsigned int ecx = 0;
|
|
111
|
+
unsigned int edx = 0;
|
|
112
|
+
if (__get_cpuid_max(0, NULL) < CPUID_EXTENDED_FEATURES_LEAF) { return 0; }
|
|
113
|
+
if (!__get_cpuid(CPUID_FEATURES_LEAF, &eax, &ebx, &ecx, &edx)) { return 0; }
|
|
114
|
+
if ((ecx & bit_OSXSAVE) == 0 || (ecx & bit_AVX) == 0) { return 0; }
|
|
115
|
+
if ((enabled_register_state() & XCR0_SSE_AND_AVX_STATE) != XCR0_SSE_AND_AVX_STATE) { return 0; }
|
|
116
|
+
__cpuid_count(CPUID_EXTENDED_FEATURES_LEAF, 0, eax, ebx, ecx, edx);
|
|
117
|
+
return (ebx & bit_AVX2) != 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static int uses_avx2(void) {
|
|
121
|
+
int state = atomic_load_explicit(&avx2_state, memory_order_relaxed);
|
|
122
|
+
if (state == AVX2_UNKNOWN) {
|
|
123
|
+
state = processor_runs_avx2() ? AVX2_PRESENT : AVX2_ABSENT;
|
|
124
|
+
atomic_store_explicit(&avx2_state, state, memory_order_relaxed);
|
|
125
|
+
}
|
|
126
|
+
return state == AVX2_PRESENT;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
__attribute__((target("avx2"))) static float dot_in_wide_lanes(const float *lhs, const float *rhs, uint32_t dimension) {
|
|
130
|
+
uint32_t unrolled_end = whole_steps(dimension, UNROLLED_WIDTH);
|
|
131
|
+
__m256 first_half = _mm256_setzero_ps();
|
|
132
|
+
__m256 second_half = _mm256_setzero_ps();
|
|
133
|
+
uint32_t offset = 0;
|
|
134
|
+
for (; offset < unrolled_end; offset += UNROLLED_WIDTH) {
|
|
135
|
+
first_half = _mm256_add_ps(first_half, _mm256_mul_ps(_mm256_loadu_ps(lhs + offset), _mm256_loadu_ps(rhs + offset)));
|
|
136
|
+
second_half = _mm256_add_ps(second_half, _mm256_mul_ps(_mm256_loadu_ps(lhs + offset + WIDE_LANE_WIDTH),
|
|
137
|
+
_mm256_loadu_ps(rhs + offset + WIDE_LANE_WIDTH)));
|
|
138
|
+
}
|
|
139
|
+
unrolled_dot dot = {{_mm256_castps256_ps128(first_half), _mm256_extractf128_ps(first_half, 1),
|
|
140
|
+
_mm256_castps256_ps128(second_half), _mm256_extractf128_ps(second_half, 1)},
|
|
141
|
+
offset};
|
|
142
|
+
return finish_dot(lhs, rhs, dimension, dot);
|
|
143
|
+
}
|
|
144
|
+
#endif
|
|
145
|
+
|
|
146
|
+
static float dot_in_narrow_lanes(const float *lhs, const float *rhs, uint32_t dimension) {
|
|
147
|
+
uint32_t unrolled_end = whole_steps(dimension, UNROLLED_WIDTH);
|
|
148
|
+
lanes sum0 = lanes_zero();
|
|
149
|
+
lanes sum1 = lanes_zero();
|
|
150
|
+
lanes sum2 = lanes_zero();
|
|
151
|
+
lanes sum3 = lanes_zero();
|
|
152
|
+
uint32_t offset = 0;
|
|
153
|
+
for (; offset < unrolled_end; offset += UNROLLED_WIDTH) {
|
|
154
|
+
sum0 = lanes_add(sum0, lane_product(lhs + offset, rhs + offset, 0));
|
|
155
|
+
sum1 = lanes_add(sum1, lane_product(lhs + offset, rhs + offset, 1));
|
|
156
|
+
sum2 = lanes_add(sum2, lane_product(lhs + offset, rhs + offset, 2));
|
|
157
|
+
sum3 = lanes_add(sum3, lane_product(lhs + offset, rhs + offset, 3));
|
|
158
|
+
}
|
|
159
|
+
unrolled_dot dot = {{sum0, sum1, sum2, sum3}, offset};
|
|
160
|
+
return finish_dot(lhs, rhs, dimension, dot);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
float kernel_dot(const float *lhs, const float *rhs, uint32_t dimension) {
|
|
164
|
+
#ifdef NARSIL_AVX2_DOT
|
|
165
|
+
if (uses_avx2()) { return dot_in_wide_lanes(lhs, rhs, dimension); }
|
|
166
|
+
#endif
|
|
167
|
+
return dot_in_narrow_lanes(lhs, rhs, dimension);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
float kernel_squared_distance(const float *lhs, const float *rhs, uint32_t dimension) {
|
|
171
|
+
uint32_t lanes_end = whole_steps(dimension, LANE_WIDTH);
|
|
172
|
+
lanes running = lanes_zero();
|
|
173
|
+
uint32_t offset = 0;
|
|
174
|
+
for (; offset < lanes_end; offset += LANE_WIDTH) {
|
|
175
|
+
lanes difference = lanes_sub(lanes_load(lhs + offset), lanes_load(rhs + offset));
|
|
176
|
+
running = lanes_add(running, lanes_mul(difference, difference));
|
|
177
|
+
}
|
|
178
|
+
float sum = lanes_sum_in_pairs(running);
|
|
179
|
+
for (; offset < dimension; offset++) {
|
|
180
|
+
float difference = lhs[offset] - rhs[offset];
|
|
181
|
+
sum = sum + (difference * difference);
|
|
182
|
+
}
|
|
183
|
+
return sum;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
float kernel_magnitude(const float *values, uint32_t dimension) {
|
|
187
|
+
uint32_t lanes_end = whole_steps(dimension, LANE_WIDTH);
|
|
188
|
+
lanes running = lanes_zero();
|
|
189
|
+
uint32_t offset = 0;
|
|
190
|
+
for (; offset < lanes_end; offset += LANE_WIDTH) {
|
|
191
|
+
lanes loaded = lanes_load(values + offset);
|
|
192
|
+
running = lanes_add(running, lanes_mul(loaded, loaded));
|
|
193
|
+
}
|
|
194
|
+
float sum = lanes_sum_in_pairs(running);
|
|
195
|
+
for (; offset < dimension; offset++) { sum = sum + (values[offset] * values[offset]); }
|
|
196
|
+
return sqrtf(sum);
|
|
197
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include <stddef.h>
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#define VECTOR_BYTES 16U
|
|
8
|
+
#define WORD_BYTES 8U
|
|
9
|
+
#define LOW_NIBBLE_MASK 15U
|
|
10
|
+
#define NIBBLE_BITS 4
|
|
11
|
+
#define TWO_BIT_LOW_BITS UINT64_C(0x5555555555555555)
|
|
12
|
+
|
|
13
|
+
#ifdef NARSIL_NEON
|
|
14
|
+
#include <arm_neon.h>
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#ifdef NARSIL_SSE2
|
|
18
|
+
#include <emmintrin.h>
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
#ifdef NARSIL_SSE2
|
|
22
|
+
static inline __m128i load_bytes(const uint8_t *source) {
|
|
23
|
+
__m128i loaded;
|
|
24
|
+
memcpy(&loaded, source, sizeof loaded);
|
|
25
|
+
return loaded;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static inline uint32_t sum_of_lanes(__m128i running) {
|
|
29
|
+
uint32_t lane[4];
|
|
30
|
+
memcpy(lane, &running, sizeof lane);
|
|
31
|
+
return lane[0] + lane[1] + lane[2] + lane[3];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static inline __m128i widened_products(__m128i lhs, __m128i rhs) {
|
|
35
|
+
__m128i zero = _mm_setzero_si128();
|
|
36
|
+
return _mm_add_epi32(_mm_madd_epi16(_mm_unpacklo_epi8(lhs, zero), _mm_unpacklo_epi8(rhs, zero)),
|
|
37
|
+
_mm_madd_epi16(_mm_unpackhi_epi8(lhs, zero), _mm_unpackhi_epi8(rhs, zero)));
|
|
38
|
+
}
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
static uint32_t nibble_pair_products(const uint8_t *first, const uint8_t *second, uint32_t bytes) {
|
|
42
|
+
uint32_t total = 0;
|
|
43
|
+
uint32_t offset = 0;
|
|
44
|
+
#ifdef NARSIL_NEON
|
|
45
|
+
uint8x16_t mask = vdupq_n_u8(LOW_NIBBLE_MASK);
|
|
46
|
+
uint32x4_t running = vdupq_n_u32(0);
|
|
47
|
+
for (; offset + VECTOR_BYTES <= bytes; offset += VECTOR_BYTES) {
|
|
48
|
+
uint8x16_t first_bytes = vld1q_u8(first + offset);
|
|
49
|
+
uint8x16_t second_bytes = vld1q_u8(second + offset);
|
|
50
|
+
uint8x16_t first_low = vandq_u8(first_bytes, mask);
|
|
51
|
+
uint8x16_t first_high = vshrq_n_u8(first_bytes, NIBBLE_BITS);
|
|
52
|
+
uint8x16_t second_low = vandq_u8(second_bytes, mask);
|
|
53
|
+
uint8x16_t second_high = vshrq_n_u8(second_bytes, NIBBLE_BITS);
|
|
54
|
+
uint16x8_t lower = vmlal_u8(vmull_u8(vget_low_u8(first_low), vget_low_u8(second_low)), vget_low_u8(first_high),
|
|
55
|
+
vget_low_u8(second_high));
|
|
56
|
+
uint16x8_t upper = vmlal_u8(vmull_u8(vget_high_u8(first_low), vget_high_u8(second_low)), vget_high_u8(first_high),
|
|
57
|
+
vget_high_u8(second_high));
|
|
58
|
+
running = vpadalq_u16(vpadalq_u16(running, lower), upper);
|
|
59
|
+
}
|
|
60
|
+
total = vaddvq_u32(running);
|
|
61
|
+
#elif defined(NARSIL_SSE2)
|
|
62
|
+
__m128i mask = _mm_set1_epi8((char)LOW_NIBBLE_MASK);
|
|
63
|
+
__m128i running = _mm_setzero_si128();
|
|
64
|
+
for (; offset + VECTOR_BYTES <= bytes; offset += VECTOR_BYTES) {
|
|
65
|
+
__m128i first_bytes = load_bytes(first + offset);
|
|
66
|
+
__m128i second_bytes = load_bytes(second + offset);
|
|
67
|
+
__m128i low = widened_products(_mm_and_si128(first_bytes, mask), _mm_and_si128(second_bytes, mask));
|
|
68
|
+
__m128i high = widened_products(_mm_and_si128(_mm_srli_epi16(first_bytes, NIBBLE_BITS), mask),
|
|
69
|
+
_mm_and_si128(_mm_srli_epi16(second_bytes, NIBBLE_BITS), mask));
|
|
70
|
+
running = _mm_add_epi32(running, _mm_add_epi32(low, high));
|
|
71
|
+
}
|
|
72
|
+
total = sum_of_lanes(running);
|
|
73
|
+
#endif
|
|
74
|
+
for (; offset < bytes; offset++) {
|
|
75
|
+
total += ((uint32_t)(first[offset] & LOW_NIBBLE_MASK) * (uint32_t)(second[offset] & LOW_NIBBLE_MASK)) +
|
|
76
|
+
((uint32_t)(first[offset] >> NIBBLE_BITS) * (uint32_t)(second[offset] >> NIBBLE_BITS));
|
|
77
|
+
}
|
|
78
|
+
return total;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
INLINED_IN_EACH_KERNEL uint32_t shared_bits(uint64_t first, uint64_t second) {
|
|
82
|
+
return (uint32_t)__builtin_popcountll(first & second);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
typedef struct {
|
|
86
|
+
uint64_t first;
|
|
87
|
+
uint64_t second;
|
|
88
|
+
} word_pair;
|
|
89
|
+
|
|
90
|
+
INLINED_IN_EACH_KERNEL uint32_t two_bit_products(word_pair words) {
|
|
91
|
+
uint64_t first_low = words.first & TWO_BIT_LOW_BITS;
|
|
92
|
+
uint64_t first_high = (words.first >> 1) & TWO_BIT_LOW_BITS;
|
|
93
|
+
uint64_t second_low = words.second & TWO_BIT_LOW_BITS;
|
|
94
|
+
uint64_t second_high = (words.second >> 1) & TWO_BIT_LOW_BITS;
|
|
95
|
+
return shared_bits(first_low, second_low) + (2 * shared_bits(first_low, second_high)) +
|
|
96
|
+
(2 * shared_bits(first_high, second_low)) + (4 * shared_bits(first_high, second_high));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
INLINED_IN_EACH_KERNEL uint32_t narrow_word_products(word_pair words, uint32_t bits) {
|
|
100
|
+
return bits == 1 ? shared_bits(words.first, words.second) : two_bit_products(words);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
INLINED_IN_EACH_KERNEL uint32_t narrow_pair_products(packed_code first, const uint8_t *second) {
|
|
104
|
+
uint32_t total = 0;
|
|
105
|
+
uint32_t offset = 0;
|
|
106
|
+
for (; offset + WORD_BYTES <= first.byte_count; offset += WORD_BYTES) {
|
|
107
|
+
word_pair words = {0, 0};
|
|
108
|
+
memcpy(&words.first, first.bytes + offset, sizeof words.first);
|
|
109
|
+
memcpy(&words.second, second + offset, sizeof words.second);
|
|
110
|
+
total += narrow_word_products(words, first.bits);
|
|
111
|
+
}
|
|
112
|
+
for (; offset < first.byte_count; offset++) {
|
|
113
|
+
word_pair words = {first.bytes[offset], second[offset]};
|
|
114
|
+
total += narrow_word_products(words, first.bits);
|
|
115
|
+
}
|
|
116
|
+
return total;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#ifdef NARSIL_HARDWARE_POPCOUNT
|
|
120
|
+
__attribute__((target("popcnt"))) static uint32_t narrow_pair_products_with_popcnt(packed_code first,
|
|
121
|
+
const uint8_t *second) {
|
|
122
|
+
return narrow_pair_products(first, second);
|
|
123
|
+
}
|
|
124
|
+
#endif
|
|
125
|
+
|
|
126
|
+
uint32_t kernel_pair_products(packed_code first, const uint8_t *second, uint32_t dimension) {
|
|
127
|
+
if (first.bits == BITS_PER_BYTE) { return kernel_products_8x8(first.bytes, second, dimension); }
|
|
128
|
+
if (first.bits == NIBBLE_BITS) { return nibble_pair_products(first.bytes, second, first.byte_count); }
|
|
129
|
+
#ifdef NARSIL_HARDWARE_POPCOUNT
|
|
130
|
+
if (uses_hardware_popcount()) { return narrow_pair_products_with_popcnt(first, second); }
|
|
131
|
+
#endif
|
|
132
|
+
return narrow_pair_products(first, second);
|
|
133
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
static _Atomic(int32_t) *incoming_word(const walk_context *context, int32_t ordinal) {
|
|
11
|
+
if (ordinal < 0 || context->graph->incoming == NULL) { return NULL; }
|
|
12
|
+
if (ordinal >= load_word(context->graph->header, NARSIL_GRAPH_WORD_SLOT_CAPACITY)) { return NULL; }
|
|
13
|
+
return atomic_word(context->graph->incoming, (uint32_t)ordinal);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
int32_t incoming_count(const walk_context *context, int32_t ordinal) {
|
|
17
|
+
_Atomic(int32_t) *word = incoming_word(context, ordinal);
|
|
18
|
+
return word == NULL ? 0 : atomic_load(word);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void note_linked(const walk_context *context, int32_t ordinal) {
|
|
22
|
+
_Atomic(int32_t) *word = incoming_word(context, ordinal);
|
|
23
|
+
if (word != NULL) { atomic_fetch_add(word, 1); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void note_nearest_link_due(const walk_context *context, int32_t ordinal) {
|
|
27
|
+
if (incoming_word(context, ordinal) == NULL) { return; }
|
|
28
|
+
atomic_store(atomic_byte(context->graph->nearest_link_due, (uint32_t)ordinal), 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void note_unlinked(const walk_context *context, int32_t ordinal) {
|
|
32
|
+
_Atomic(int32_t) *word = incoming_word(context, ordinal);
|
|
33
|
+
if (word == NULL) { return; }
|
|
34
|
+
note_nearest_link_due(context, ordinal);
|
|
35
|
+
int32_t seen = atomic_load(word);
|
|
36
|
+
while (seen > 0 && !atomic_compare_exchange_weak(word, &seen, seen - 1)) {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void note_node_left_unlinked(const walk_context *context) {
|
|
40
|
+
atomic_store(atomic_word(context->graph->header, NARSIL_GRAPH_WORD_UNLINKED_NODES), 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
int release_unless_last(const walk_context *context, int32_t ordinal) {
|
|
44
|
+
_Atomic(int32_t) *word = incoming_word(context, ordinal);
|
|
45
|
+
if (word == NULL) { return 1; }
|
|
46
|
+
int32_t seen = atomic_load(word);
|
|
47
|
+
while (seen > 1) {
|
|
48
|
+
if (!atomic_compare_exchange_weak(word, &seen, seen - 1)) { continue; }
|
|
49
|
+
note_nearest_link_due(context, ordinal);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
int is_live(const walk_context *context, int32_t ordinal) {
|
|
56
|
+
return node_top_layer(context, ordinal) >= 0 && !is_tombstoned(context, ordinal);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
narsil_status reserve_last_links(narsil_workspace *workspace) {
|
|
60
|
+
return list_reserve(&workspace->dropped_links, MAX_REPAIR_CANDIDATES);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
int list_holds(held_links list, int32_t ordinal) {
|
|
64
|
+
for (uint32_t i = 0; i < list.count; i++) {
|
|
65
|
+
if (list.ordinals[i] == ordinal) { return 1; }
|
|
66
|
+
}
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static int was_held(held_links held, int32_t ordinal) { return held.every_candidate || list_holds(held, ordinal); }
|
|
71
|
+
|
|
72
|
+
static double distance_in(const scored_list *sorted, int32_t ordinal) {
|
|
73
|
+
for (uint32_t i = 0; i < sorted->count; i++) {
|
|
74
|
+
if (sorted->ordinals[i] == ordinal) { return sorted->distances[i]; }
|
|
75
|
+
}
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static void collect_dropped(const scored_list *sorted, const int32_t *kept, uint32_t kept_count, scored_list *dropped) {
|
|
80
|
+
dropped->count = 0;
|
|
81
|
+
held_links chosen = {kept, kept_count, 0};
|
|
82
|
+
for (uint32_t i = 0; i < sorted->count && dropped->count < dropped->capacity; i++) {
|
|
83
|
+
if (list_holds(chosen, sorted->ordinals[i])) { continue; }
|
|
84
|
+
dropped->ordinals[dropped->count] = sorted->ordinals[i];
|
|
85
|
+
dropped->distances[dropped->count] = sorted->distances[i];
|
|
86
|
+
dropped->count += 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static int replace_farthest_shared(const walk_context *context, int32_t *kept, uint32_t kept_count, held_links held,
|
|
91
|
+
scored_node replacement) {
|
|
92
|
+
narsil_workspace *workspace = context->workspace;
|
|
93
|
+
double *distances = workspace->kept_distances;
|
|
94
|
+
uint8_t *tried = workspace->replacement_tried;
|
|
95
|
+
memset(tried, 0, kept_count);
|
|
96
|
+
for (;;) {
|
|
97
|
+
int64_t farthest = -1;
|
|
98
|
+
for (uint32_t k = 0; k < kept_count; k++) {
|
|
99
|
+
if (tried[k] || incoming_count(context, kept[k]) < 2) { continue; }
|
|
100
|
+
if (farthest == -1 || distances[k] > distances[farthest]) { farthest = k; }
|
|
101
|
+
}
|
|
102
|
+
if (farthest == -1) { return 0; }
|
|
103
|
+
tried[farthest] = 1;
|
|
104
|
+
int32_t replaced = kept[farthest];
|
|
105
|
+
if (was_held(held, replaced) && !release_unless_last(context, replaced)) { continue; }
|
|
106
|
+
kept[farthest] = replacement.ordinal;
|
|
107
|
+
distances[farthest] = replacement.distance;
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
uint32_t write_keeping_last_links(const walk_context *context, int32_t ordinal, const scored_list *sorted,
|
|
113
|
+
int32_t *kept, uint32_t kept_count, held_links held) {
|
|
114
|
+
narsil_workspace *workspace = context->workspace;
|
|
115
|
+
uint32_t limit = list_limit(context->graph, 0);
|
|
116
|
+
scored_list *dropped = &workspace->dropped_links;
|
|
117
|
+
collect_dropped(sorted, kept, kept_count, dropped);
|
|
118
|
+
for (uint32_t k = 0; k < kept_count; k++) { workspace->kept_distances[k] = distance_in(sorted, kept[k]); }
|
|
119
|
+
uint32_t count = kept_count;
|
|
120
|
+
for (uint32_t i = 0; i < dropped->count; i++) {
|
|
121
|
+
int32_t candidate = dropped->ordinals[i];
|
|
122
|
+
if (!was_held(held, candidate)) { continue; }
|
|
123
|
+
if (!is_live(context, candidate)) {
|
|
124
|
+
note_unlinked(context, candidate);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (release_unless_last(context, candidate)) { continue; }
|
|
128
|
+
if (count < limit && count < MAX_NEIGHBOURS_PER_LIST) {
|
|
129
|
+
kept[count] = candidate;
|
|
130
|
+
workspace->kept_distances[count] = dropped->distances[i];
|
|
131
|
+
count += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
scored_node replacement = {candidate, dropped->distances[i]};
|
|
135
|
+
if (replace_farthest_shared(context, kept, count, held, replacement)) { continue; }
|
|
136
|
+
note_unlinked(context, candidate);
|
|
137
|
+
note_node_left_unlinked(context);
|
|
138
|
+
}
|
|
139
|
+
for (uint32_t k = 0; k < count; k++) {
|
|
140
|
+
if (!was_held(held, kept[k])) { note_linked(context, kept[k]); }
|
|
141
|
+
}
|
|
142
|
+
node_layer position = {ordinal, 0};
|
|
143
|
+
list_replace(context, position, kept, count);
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stdatomic.h>
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
|
|
11
|
+
#define INITIAL_LIST_CAPACITY 256
|
|
12
|
+
#define FARTHEST_REJECTED_LINKS_KEPT 1U
|
|
13
|
+
|
|
14
|
+
narsil_status list_reserve(scored_list *list, uint32_t capacity) {
|
|
15
|
+
if (capacity <= list->capacity) { return NARSIL_OK; }
|
|
16
|
+
uint32_t held = list->capacity == 0 ? INITIAL_LIST_CAPACITY : list->capacity;
|
|
17
|
+
uint32_t next = grown_capacity(held, capacity);
|
|
18
|
+
int32_t *ordinals = realloc(list->ordinals, (size_t)next * sizeof *ordinals);
|
|
19
|
+
if (ordinals == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
20
|
+
list->ordinals = ordinals;
|
|
21
|
+
double *distances = realloc(list->distances, (size_t)next * sizeof *distances);
|
|
22
|
+
if (distances == NULL) { return NARSIL_OUT_OF_MEMORY; }
|
|
23
|
+
list->distances = distances;
|
|
24
|
+
list->capacity = next;
|
|
25
|
+
return NARSIL_OK;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void list_release(scored_list *list) {
|
|
29
|
+
free(list->ordinals);
|
|
30
|
+
free(list->distances);
|
|
31
|
+
list->ordinals = NULL;
|
|
32
|
+
list->distances = NULL;
|
|
33
|
+
list->capacity = 0;
|
|
34
|
+
list->count = 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void list_sort_nearest_first(scored_list *list) {
|
|
38
|
+
for (uint32_t i = 1; i < list->count; i++) {
|
|
39
|
+
int32_t ordinal = list->ordinals[i];
|
|
40
|
+
double distance = list->distances[i];
|
|
41
|
+
uint32_t slot = i;
|
|
42
|
+
while (slot > 0 && list->distances[slot - 1] > distance) {
|
|
43
|
+
list->distances[slot] = list->distances[slot - 1];
|
|
44
|
+
list->ordinals[slot] = list->ordinals[slot - 1];
|
|
45
|
+
slot -= 1;
|
|
46
|
+
}
|
|
47
|
+
list->distances[slot] = distance;
|
|
48
|
+
list->ordinals[slot] = ordinal;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
uint32_t stored_count(const int32_t *list, uint32_t capacity) {
|
|
53
|
+
if (list[0] < 0) { return 0; }
|
|
54
|
+
return (uint32_t)list[0] > capacity ? capacity : (uint32_t)list[0];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void list_add(const walk_context *context, node_layer position, int32_t neighbour) {
|
|
58
|
+
uint32_t capacity = 0;
|
|
59
|
+
int32_t *list = list_at(context, position, &capacity);
|
|
60
|
+
if (list == NULL) { return; }
|
|
61
|
+
uint32_t count = stored_count(list, capacity);
|
|
62
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
63
|
+
if (list[i] == neighbour) { return; }
|
|
64
|
+
}
|
|
65
|
+
if (count + 1 > capacity) { return; }
|
|
66
|
+
list[count + 1] = neighbour;
|
|
67
|
+
list[0] = (int32_t)(count + 1);
|
|
68
|
+
if (position.layer == 0) { note_linked(context, neighbour); }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void list_take_out(const walk_context *context, node_layer position, int32_t neighbour) {
|
|
72
|
+
uint32_t capacity = 0;
|
|
73
|
+
int32_t *list = list_at(context, position, &capacity);
|
|
74
|
+
if (list == NULL) { return; }
|
|
75
|
+
uint32_t count = stored_count(list, capacity);
|
|
76
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
77
|
+
if (list[i] != neighbour) { continue; }
|
|
78
|
+
for (uint32_t moved = i; moved < count; moved++) { list[moved] = list[moved + 1]; }
|
|
79
|
+
list[0] = (int32_t)(count - 1);
|
|
80
|
+
if (position.layer == 0) { note_unlinked(context, neighbour); }
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void list_write_counted(const walk_context *context, node_layer position, const int32_t *neighbours, uint32_t count) {
|
|
86
|
+
uint32_t capacity = 0;
|
|
87
|
+
int32_t *list = list_at(context, position, &capacity);
|
|
88
|
+
if (list == NULL) { return; }
|
|
89
|
+
if (position.layer > 0) {
|
|
90
|
+
list_replace(context, position, neighbours, count);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
held_links held = {list + 1, stored_count(list, capacity), 0};
|
|
94
|
+
held_links written = {neighbours, count > capacity ? capacity : count, 0};
|
|
95
|
+
for (uint32_t i = 0; i < held.count; i++) {
|
|
96
|
+
if (!list_holds(written, held.ordinals[i])) { note_unlinked(context, held.ordinals[i]); }
|
|
97
|
+
}
|
|
98
|
+
for (uint32_t i = 0; i < written.count; i++) {
|
|
99
|
+
if (!list_holds(held, written.ordinals[i])) { note_linked(context, written.ordinals[i]); }
|
|
100
|
+
}
|
|
101
|
+
list_replace(context, position, neighbours, count);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
void list_replace(const walk_context *context, node_layer position, const int32_t *neighbours, uint32_t count) {
|
|
105
|
+
uint32_t capacity = 0;
|
|
106
|
+
int32_t *list = list_at(context, position, &capacity);
|
|
107
|
+
if (list == NULL) { return; }
|
|
108
|
+
uint32_t kept = count > capacity ? capacity : count;
|
|
109
|
+
for (uint32_t i = 0; i < kept; i++) { list[i + 1] = neighbours[i]; }
|
|
110
|
+
list[0] = (int32_t)kept;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static uint32_t keep_farthest_rejected(const scored_list *sorted, uint32_t limit, int32_t *selected, uint32_t count) {
|
|
114
|
+
if (count * 2 >= limit) { return count; }
|
|
115
|
+
uint32_t kept = 0;
|
|
116
|
+
for (uint32_t i = sorted->count; i > 0 && kept < FARTHEST_REJECTED_LINKS_KEPT; i--) {
|
|
117
|
+
if (count >= limit) { return count; }
|
|
118
|
+
int32_t candidate = sorted->ordinals[i - 1];
|
|
119
|
+
held_links chosen = {selected, count, 0};
|
|
120
|
+
if (list_holds(chosen, candidate)) { continue; }
|
|
121
|
+
selected[count] = candidate;
|
|
122
|
+
count += 1;
|
|
123
|
+
kept += 1;
|
|
124
|
+
}
|
|
125
|
+
return count;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
uint32_t select_neighbours(const walk_context *context, scored_list *candidates, uint32_t limit, int32_t *selected) {
|
|
129
|
+
list_sort_nearest_first(candidates);
|
|
130
|
+
uint32_t count = 0;
|
|
131
|
+
for (uint32_t i = 0; i < candidates->count && count < limit; i++) {
|
|
132
|
+
int32_t candidate = candidates->ordinals[i];
|
|
133
|
+
double distance = candidates->distances[i];
|
|
134
|
+
int accepted = 1;
|
|
135
|
+
for (uint32_t chosen = 0; chosen < count && accepted; chosen++) {
|
|
136
|
+
if (distance >= pair_distance(context, candidate, selected[chosen])) { accepted = 0; }
|
|
137
|
+
}
|
|
138
|
+
if (!accepted) { continue; }
|
|
139
|
+
selected[count] = candidate;
|
|
140
|
+
count += 1;
|
|
141
|
+
}
|
|
142
|
+
return keep_farthest_rejected(candidates, limit, selected, count);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void list_prune(const walk_context *context, node_layer position) {
|
|
146
|
+
uint32_t capacity = 0;
|
|
147
|
+
int32_t *list = list_at(context, position, &capacity);
|
|
148
|
+
if (list == NULL) { return; }
|
|
149
|
+
uint32_t limit = list_limit(context->graph, position.layer);
|
|
150
|
+
uint32_t count = stored_count(list, capacity);
|
|
151
|
+
if (count <= limit) { return; }
|
|
152
|
+
scored_list *candidates = &context->workspace->link_candidates;
|
|
153
|
+
if (list_reserve(candidates, count) != NARSIL_OK) { return; }
|
|
154
|
+
candidates->count = 0;
|
|
155
|
+
for (uint32_t i = 1; i <= count; i++) {
|
|
156
|
+
double distance = pair_distance(context, position.ordinal, list[i]);
|
|
157
|
+
if (distance == HUGE_VAL) {
|
|
158
|
+
if (position.layer == 0) { note_unlinked(context, list[i]); }
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
candidates->ordinals[candidates->count] = list[i];
|
|
162
|
+
candidates->distances[candidates->count] = distance;
|
|
163
|
+
candidates->count += 1;
|
|
164
|
+
}
|
|
165
|
+
uint32_t kept = select_neighbours(context, candidates, limit, context->workspace->kept);
|
|
166
|
+
if (position.layer == 0) {
|
|
167
|
+
held_links every_candidate = {NULL, 0, 1};
|
|
168
|
+
(void)write_keeping_last_links(context, position.ordinal, candidates, context->workspace->kept, kept,
|
|
169
|
+
every_candidate);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
list_replace(context, position, context->workspace->kept, kept);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
int32_t highest_node(const walk_context *context, int include_tombstoned) {
|
|
176
|
+
int32_t best = NO_NODE;
|
|
177
|
+
int32_t best_layer = -1;
|
|
178
|
+
for (uint32_t ordinal = 0; ordinal < context->slots; ordinal++) {
|
|
179
|
+
int32_t level = node_top_layer(context, (int32_t)ordinal);
|
|
180
|
+
if (level < 0) { continue; }
|
|
181
|
+
if (!include_tombstoned && atomic_load(atomic_byte(context->graph->tombstones, ordinal)) == 1) { continue; }
|
|
182
|
+
if (level > best_layer) {
|
|
183
|
+
best_layer = level;
|
|
184
|
+
best = (int32_t)ordinal;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return best;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
void store_entry_point(const walk_context *context, int32_t ordinal) {
|
|
191
|
+
int32_t *header = context->graph->header;
|
|
192
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_ENTRY_POINT), ordinal);
|
|
193
|
+
atomic_store(atomic_word(header, NARSIL_GRAPH_WORD_TOP_LAYER),
|
|
194
|
+
ordinal == NO_NODE ? -1 : node_top_layer(context, ordinal));
|
|
195
|
+
}
|