Skip to content

Commit 2e89f5c

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 8689b16 commit 2e89f5c

File tree

12 files changed

+509
-5
lines changed

12 files changed

+509
-5
lines changed

BUILD.bazel

Lines changed: 45 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,50 @@ 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_delete",
794+
":operator_type",
795+
":xnnpack_h",
796+
],
797+
)
798+
799+
xnnpack_cc_library(
800+
name = "fingerprint_check",
801+
srcs = ["src/xnnpack/fingerprint_check.c"],
802+
deps = [
803+
":fingerprint_cache",
804+
":fingerprint_id",
805+
":operators",
806+
":xnnpack_h",
807+
],
808+
)
809+
810+
xnnpack_cc_library(
811+
name = "operator_delete",
812+
srcs = ["src/operator-delete.c"],
813+
deps = [
814+
":allocator",
815+
":logging",
816+
":operator_h",
817+
":operator_utils",
818+
":params",
819+
":xnnpack_h",
820+
],
821+
)
822+
778823
xnnpack_cc_library(
779824
name = "operators",
780825
srcs = OPERATOR_SRCS,

CMakeLists.txt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,9 @@ 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-operator-delete OBJECT src/operator-delete.c)
943+
ADD_LIBRARY(xnnpack-fingerprint-cache OBJECT src/operators/fingerprint_cache.c)
944+
ADD_LIBRARY(xnnpack-fingerprint-check OBJECT src/xnnpack/fingerprint_check.c)
942945
ADD_LIBRARY(xnnpack-memory OBJECT src/memory.c)
943946
ADD_LIBRARY(xnnpack-microkernel-utils OBJECT src/microkernel-utils.c)
944947
ADD_LIBRARY(xnnpack-mutex OBJECT src/mutex.c)
@@ -962,21 +965,25 @@ IF(XNNPACK_BUILD_LIBRARY)
962965
TARGET_LINK_LIBRARIES(xnnpack-allocator PRIVATE xnnpack-base xnnpack-logging)
963966
TARGET_LINK_LIBRARIES(xnnpack-cache PRIVATE xnnpack-base xnnpack-logging)
964967
TARGET_LINK_LIBRARIES(xnnpack-datatype PRIVATE xnnpack-base)
968+
TARGET_LINK_LIBRARIES(xnnpack-operator-delete PRIVATE xnnpack-base xnnpack-logging)
965969
TARGET_LINK_LIBRARIES(xnnpack-memory PRIVATE xnnpack-base xnnpack-logging)
966970
TARGET_LINK_LIBRARIES(xnnpack-microkernel-utils PRIVATE xnnpack-base xnnpack-hardware-config xnnpack-logging)
967971
TARGET_LINK_LIBRARIES(xnnpack-mutex PRIVATE xnnpack-base xnnpack-logging)
968972
TARGET_LINK_LIBRARIES(xnnpack-operators PRIVATE xnnpack-base xnnpack-allocator xnnpack-indirection
969973
xnnpack-logging xnnpack-microkernel-utils xnnpack-normalization xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing
970974
xnnpack-reference-ukernels xnnpack-datatype)
975+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-cache PRIVATE xnnpack-base xnnpack-cache xnnpack-mutex xnnpack-operator-delete)
976+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-check PRIVATE xnnpack-base xnnpack-fingerprint-cache xnnpack-operators)
971977
TARGET_LINK_LIBRARIES(xnnpack-operator-run PRIVATE xnnpack-base xnnpack-logging)
972978
TARGET_LINK_LIBRARIES(xnnpack-operator-utils PRIVATE xnnpack-base xnnpack-logging)
973979
TARGET_LINK_LIBRARIES(xnnpack-reference-ukernels PRIVATE xnnpack-base xnnpack-datatype)
974980
TARGET_LINK_LIBRARIES(xnnpack-subgraph PRIVATE xnnpack-base xnnpack-allocator xnnpack-logging xnnpack-memory xnnpack-mutex xnnpack-operators xnnpack-operator-run xnnpack-datatype)
975981
TARGET_LINK_LIBRARIES(XNNPACK PRIVATE xnnpack-base xnnpack-allocator xnnpack-cache
976982
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)
983+
xnnpack-mutex xnnpack-normalization xnnpack-operators xnnpack-operator-run
984+
xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing xnnpack-fingerprint-cache xnnpack-microkernels-prod
985+
xnnpack-subgraph xnnpack-datatype xnnpack-reference-ukernels)
986+
TARGET_LINK_LIBRARIES(XNNPACK PUBLIC pthreadpool xnnpack-logging xnnpack-fingerprint-check)
980987
SET_TARGET_PROPERTIES(XNNPACK PROPERTIES C_EXTENSIONS YES)
981988
ENDIF()
982989
IF(NOT MSVC)

build_srcs.bzl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ Lists of target-specific sources used to build XNNPACK.
1212
"""
1313

1414
OPERATOR_SRCS = [
15-
"src/operator-delete.c",
1615
"src/operator-run.c",
1716
"src/operators/argmax-pooling-nhwc.c",
1817
"src/operators/average-pooling-nhwc.c",

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

0 commit comments

Comments
 (0)