Skip to content

Commit f2a22c7

Browse files
qukhanxnnpack-bot
authored andcommitted
Add a new fingerprinting method for XNNPack weight cache.
Warning: this new fingerprinting method is currently experimental. Every operation in XNNPack that uses the weight cache should implement a corresponding fingerprinting function. The goal of that fingerprint is to check whether the pre-computation (packing) that takes place for this op has changed and if a previous weight cache can be reused directly or not. The expected way for this to work is that a fingerprint function would assume a predetermined set of constant inputs passed to the create function and would then hash the resulting cache buffer. The `xnn_weights_cache_look_up_key` has a new field that holds the fingerprint identifier. It is set by kernel setup functions when they try to do cache look-ups. PiperOrigin-RevId: 809079455
1 parent 28a698d commit f2a22c7

File tree

11 files changed

+464
-4
lines changed

11 files changed

+464
-4
lines changed

BUILD.bazel

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ xnnpack_cc_library(
142142
"include/xnnpack.h",
143143
],
144144
deps = [
145+
":fingerprint_id",
145146
"@pthreadpool",
146147
],
147148
)
@@ -775,6 +776,36 @@ xnnpack_cc_library(
775776
],
776777
)
777778

779+
xnnpack_cc_library(
780+
name = "fingerprint_id",
781+
hdrs = ["src/operators/fingerprint_id.h"],
782+
)
783+
784+
xnnpack_cc_library(
785+
name = "fingerprint_cache",
786+
srcs = ["src/operators/fingerprint_cache.c"],
787+
hdrs = ["src/operators/fingerprint_cache.h"],
788+
deps = [
789+
":cache",
790+
":fingerprint_id",
791+
":init_once",
792+
":mutex",
793+
":operator_type",
794+
":xnnpack_h",
795+
],
796+
)
797+
798+
xnnpack_cc_library(
799+
name = "fingerprint_check",
800+
srcs = ["src/xnnpack/fingerprint_check.c"],
801+
deps = [
802+
":fingerprint_cache",
803+
":fingerprint_id",
804+
":operators",
805+
":xnnpack_h",
806+
],
807+
)
808+
778809
xnnpack_cc_library(
779810
name = "operators",
780811
srcs = OPERATOR_SRCS,

CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,8 @@ IF(XNNPACK_BUILD_LIBRARY)
939939
ADD_LIBRARY(xnnpack-allocator OBJECT src/allocator.c)
940940
ADD_LIBRARY(xnnpack-cache OBJECT src/cache.c)
941941
ADD_LIBRARY(xnnpack-datatype OBJECT src/datatype.c)
942+
ADD_LIBRARY(xnnpack-fingerprint-cache OBJECT src/operators/fingerprint_cache.c)
943+
ADD_LIBRARY(xnnpack-fingerprint-check OBJECT src/xnnpack/fingerprint_check.c)
942944
ADD_LIBRARY(xnnpack-memory OBJECT src/memory.c)
943945
ADD_LIBRARY(xnnpack-microkernel-utils OBJECT src/microkernel-utils.c)
944946
ADD_LIBRARY(xnnpack-mutex OBJECT src/mutex.c)
@@ -968,15 +970,18 @@ IF(XNNPACK_BUILD_LIBRARY)
968970
TARGET_LINK_LIBRARIES(xnnpack-operators PRIVATE xnnpack-base xnnpack-allocator xnnpack-indirection
969971
xnnpack-logging xnnpack-microkernel-utils xnnpack-normalization xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing
970972
xnnpack-reference-ukernels xnnpack-datatype)
973+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-cache PRIVATE xnnpacke-base xnnpack-cache xnnpack-mutex)
974+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-check PRIVATE xnnpacke-base xnnpack-fingerprint-cache xnnpack-operators)
971975
TARGET_LINK_LIBRARIES(xnnpack-operator-run PRIVATE xnnpack-base xnnpack-logging)
972976
TARGET_LINK_LIBRARIES(xnnpack-operator-utils PRIVATE xnnpack-base xnnpack-logging)
973977
TARGET_LINK_LIBRARIES(xnnpack-reference-ukernels PRIVATE xnnpack-base xnnpack-datatype)
974978
TARGET_LINK_LIBRARIES(xnnpack-subgraph PRIVATE xnnpack-base xnnpack-allocator xnnpack-logging xnnpack-memory xnnpack-mutex xnnpack-operators xnnpack-operator-run xnnpack-datatype)
975979
TARGET_LINK_LIBRARIES(XNNPACK PRIVATE xnnpack-base xnnpack-allocator xnnpack-cache
976980
xnnpack-hardware-config xnnpack-indirection xnnpack-memory xnnpack-microkernel-utils xnnpack-microparams-init
977-
xnnpack-mutex xnnpack-normalization xnnpack-operators xnnpack-operator-run xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing
978-
xnnpack-microkernels-prod xnnpack-subgraph xnnpack-datatype xnnpack-reference-ukernels)
979-
TARGET_LINK_LIBRARIES(XNNPACK PUBLIC pthreadpool xnnpack-logging)
981+
xnnpack-mutex xnnpack-normalization xnnpack-operators xnnpack-operator-run
982+
xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing xnnpack-fingerprint-cache xnnpack-microkernels-prod
983+
xnnpack-subgraph xnnpack-datatype xnnpack-reference-ukernels)
984+
TARGET_LINK_LIBRARIES(XNNPACK PUBLIC pthreadpool xnnpack-logging xnnpack-fingerprint-check)
980985
SET_TARGET_PROPERTIES(XNNPACK PROPERTIES C_EXTENSIONS YES)
981986
ENDIF()
982987
IF(NOT MSVC)

include/experimental.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <stdint.h>
1717

1818
#include "include/xnnpack.h"
19+
#include "src/operators/fingerprint_id.h"
1920

2021
#ifdef __cplusplus
2122
extern "C" {
@@ -98,6 +99,29 @@ enum xnn_status xnn_update_runtime_with_threadpool(
9899
xnn_runtime_t runtime,
99100
xnn_threadpool_t threadpool);
100101

102+
103+
typedef struct xnn_fingerprint* xnn_fingerprint_t;
104+
105+
struct xnn_fingerprint {
106+
enum xnn_fingerprint_id id;
107+
uint32_t value;
108+
};
109+
110+
/// Check whether the given configuration matches one that is currently in use.
111+
///
112+
/// @returns True if the configuration matches.
113+
bool xnn_check_fingerprint(struct xnn_fingerprint fingerprint);
114+
115+
/// Return the fingerprint corresponding to the given id or NULL if it wasn't
116+
/// set.
117+
const struct xnn_fingerprint* xnn_get_fingerprint(enum xnn_fingerprint_id id);
118+
119+
/// Set the given fingerprint.
120+
void xnn_set_fingerprint(struct xnn_fingerprint fingerprint);
121+
122+
/// Clear all fingerprints that were computed until now.
123+
void xnn_clear_fingerprints();
124+
101125
#ifdef __cplusplus
102126
} // extern "C"
103127
#endif

include/xnnpack.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Facebook, Inc. and its affiliates.
22
// All rights reserved.
33
//
4-
// Copyright 2019 Google LLC
4+
// Copyright 2019-2025 Google LLC
55
//
66
// This source code is licensed under the BSD-style license found in the
77
// LICENSE file in the root directory of this source tree.
@@ -15,6 +15,7 @@
1515
#include <stddef.h>
1616
#include <stdint.h>
1717

18+
#include "src/operators/fingerprint_id.h"
1819
#include <pthreadpool.h>
1920

2021
#ifdef __cplusplus
@@ -2297,6 +2298,8 @@ struct xnn_weights_cache_look_up_key {
22972298
const void* kernel;
22982299
/// Pointer to the original bias, could be NULL.
22992300
const void* bias;
2301+
2302+
enum xnn_fingerprint_id fingerprint_id;
23002303
};
23012304

23022305
/// A group of function pointers to manage weights cache. All functions may be

src/operators/fingerprint_cache.c

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// This source code is licensed under the BSD-style license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#include "src/operators/fingerprint_cache.h"
7+
8+
#include <assert.h>
9+
#include <stddef.h>
10+
#include <stdint.h>
11+
#include <stdlib.h>
12+
13+
#include "include/experimental.h"
14+
#include "include/xnnpack.h"
15+
#include "src/operators/fingerprint_id.h"
16+
#include "src/xnnpack/cache.h"
17+
#include "src/xnnpack/init-once.h"
18+
#include "src/xnnpack/mutex.h"
19+
#include "src/xnnpack/operator-type.h"
20+
21+
#define XNN_FINGERPRINT_MAX_COUNT 256
22+
static struct xnn_fingerprint fingerprint_vector[XNN_FINGERPRINT_MAX_COUNT];
23+
static uint32_t fingerprint_vector_size = 0;
24+
25+
static struct xnn_mutex mutex;
26+
XNN_INIT_ONCE_GUARD(mutex);
27+
28+
static void init_mutex_config() {
29+
xnn_mutex_init(&mutex);
30+
}
31+
32+
const struct xnn_fingerprint* xnn_get_fingerprint(
33+
const enum xnn_fingerprint_id id) {
34+
XNN_INIT_ONCE(mutex);
35+
uint32_t i = 0;
36+
xnn_mutex_lock(&mutex);
37+
for (; i < fingerprint_vector_size; ++i) {
38+
if (fingerprint_vector[i].id == id) {
39+
break;
40+
}
41+
}
42+
xnn_mutex_unlock(&mutex);
43+
return i < fingerprint_vector_size ? fingerprint_vector + i : NULL;
44+
}
45+
46+
void xnn_set_fingerprint(const struct xnn_fingerprint fingerprint) {
47+
XNN_INIT_ONCE(mutex);
48+
uint32_t i = 0;
49+
xnn_mutex_lock(&mutex);
50+
for (; i < fingerprint_vector_size; ++i) {
51+
if (fingerprint_vector[i].id == fingerprint.id) {
52+
fingerprint_vector[i] = fingerprint;
53+
return;
54+
}
55+
}
56+
xnn_mutex_unlock(&mutex);
57+
if (i < fingerprint_vector_size) {
58+
return;
59+
}
60+
assert(fingerprint_vector_size < XNN_FINGERPRINT_MAX_COUNT);
61+
fingerprint_vector[fingerprint_vector_size++] = fingerprint;
62+
}
63+
64+
void xnn_clear_fingerprints() {
65+
XNN_INIT_ONCE(mutex);
66+
xnn_mutex_lock(&mutex);
67+
fingerprint_vector_size = 0;
68+
xnn_mutex_unlock(&mutex);
69+
}
70+
71+
#define XNNPACK_OP_TYPE_TO_FINGERPRINT(name) \
72+
case xnn_operator_type_##name: \
73+
return xnn_fingerprint_id_##name;
74+
75+
enum xnn_fingerprint_id fingerprint_id_from_operator_type(
76+
const enum xnn_operator_type operator_type) {
77+
// LINT.IfChange(operator_to_fingerprint_id)
78+
switch (operator_type) {
79+
default:
80+
return xnn_fingerprint_id_unknown;
81+
}
82+
// LINT.ThenChange(fingerprint_id.h:fingerprint_id)
83+
}
84+
85+
#undef XNNPACK_OP_TYPE_TO_FINGERPRINT
86+
87+
// The context for an XNNPack weight cache provider that we pass to operator
88+
// `create` functions when we want to fingerprint them.
89+
struct fingerprint_cache_context {
90+
void* buffer;
91+
size_t bytes;
92+
uint32_t hash;
93+
};
94+
95+
static size_t fingerprint_cache_look_up(
96+
void* context, const struct xnn_weights_cache_look_up_key* cache_key) {
97+
return XNN_CACHE_NOT_FOUND;
98+
}
99+
100+
static void* fingerprint_cache_reserve_space(void* const context, size_t n) {
101+
struct fingerprint_cache_context* const ctx = context;
102+
void* buffer = realloc(ctx->buffer, n);
103+
if (!buffer) {
104+
return NULL;
105+
}
106+
ctx->buffer = buffer;
107+
ctx->bytes = n;
108+
return ctx->buffer;
109+
}
110+
111+
static size_t fingerprint_cache_look_up_or_insert(
112+
void* context, const struct xnn_weights_cache_look_up_key* cache_key,
113+
void* ptr, size_t size) {
114+
assert(context);
115+
struct fingerprint_cache_context* const ctx = context;
116+
ctx->hash = murmur_hash3(ptr, size, /*seed=*/ctx->hash);
117+
return 0;
118+
}
119+
120+
static bool fingerprint_cache_is_finalized(void* context) { return false; }
121+
122+
static void* fingerprint_cache_offset_to_addr(void* context, size_t offset) {
123+
assert(context);
124+
struct fingerprint_cache_context* const ctx = context;
125+
return ctx->buffer;
126+
}
127+
128+
static enum xnn_status fingerprint_cache_delete_cache(void* context) {
129+
struct fingerprint_cache_context* const ctx = context;
130+
if (ctx) {
131+
if (ctx->buffer) {
132+
free(ctx->buffer);
133+
}
134+
*ctx = (struct fingerprint_cache_context){0};
135+
}
136+
return xnn_status_success;
137+
}
138+
139+
struct fingerprint_context create_fingerprint_context_for_op(
140+
const enum xnn_operator_type operator_type) {
141+
return create_fingerprint_context(
142+
fingerprint_id_from_operator_type(operator_type));
143+
}
144+
145+
struct fingerprint_context create_fingerprint_context(
146+
const enum xnn_fingerprint_id fingerprint_id) {
147+
struct fingerprint_context context = {
148+
.status = xnn_status_uninitialized,
149+
.fingerprint_id = fingerprint_id,
150+
.cache =
151+
(struct xnn_weights_cache_provider){
152+
.context = NULL,
153+
.look_up = fingerprint_cache_look_up,
154+
.reserve_space = fingerprint_cache_reserve_space,
155+
.look_up_or_insert = fingerprint_cache_look_up_or_insert,
156+
.is_finalized = fingerprint_cache_is_finalized,
157+
.offset_to_addr = fingerprint_cache_offset_to_addr,
158+
.delete_cache = fingerprint_cache_delete_cache},
159+
};
160+
if (context.fingerprint_id == xnn_fingerprint_id_unknown) {
161+
context.status = xnn_status_unsupported_parameter;
162+
} else if (xnn_get_fingerprint(context.fingerprint_id)) {
163+
context.status = xnn_status_success;
164+
} else {
165+
// Do this after the checks to avoid a memory allocation when unnecessary.
166+
context.cache.context = calloc(1, sizeof(struct fingerprint_cache_context));
167+
}
168+
return context;
169+
}
170+
171+
static void free_fingerprint_cache_provider(
172+
struct xnn_weights_cache_provider* const provider) {
173+
if (provider) {
174+
provider->delete_cache(provider->context);
175+
if (provider->context) {
176+
free(provider->context);
177+
}
178+
}
179+
}
180+
181+
void finalize_fingerprint_context(struct fingerprint_context* const context) {
182+
assert(context);
183+
if (context->status == xnn_status_uninitialized) {
184+
xnn_set_fingerprint((struct xnn_fingerprint){
185+
.id = context->fingerprint_id,
186+
.value = fingerprint_cache_get_fingerprint(&context->cache)});
187+
}
188+
free_fingerprint_cache_provider(&context->cache);
189+
}
190+
191+
uint32_t fingerprint_cache_get_fingerprint(
192+
const struct xnn_weights_cache_provider* const provider) {
193+
assert(provider);
194+
assert(provider->context);
195+
return ((struct fingerprint_cache_context*)provider->context)->hash;
196+
}

0 commit comments

Comments
 (0)