Skip to content

Commit 0f1be92

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 43c2b57 commit 0f1be92

File tree

14 files changed

+730
-5
lines changed

14 files changed

+730
-5
lines changed

BUILD.bazel

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ xnnpack_cc_library(
140140
"include/xnnpack.h",
141141
],
142142
deps = [
143+
":fingerprint_id",
143144
"@pthreadpool",
144145
],
145146
)
@@ -773,6 +774,54 @@ xnnpack_cc_library(
773774
],
774775
)
775776

777+
xnnpack_cc_library(
778+
name = "fingerprint_id",
779+
srcs = ["src/operators/fingerprint_id.c"],
780+
hdrs = [
781+
"src/operators/fingerprint_id.h",
782+
"src/operators/fingerprint_id.h.inc",
783+
],
784+
)
785+
786+
xnnpack_cc_library(
787+
name = "fingerprint_cache",
788+
srcs = ["src/operators/fingerprint_cache.c"],
789+
hdrs = ["src/operators/fingerprint_cache.h"],
790+
deps = [
791+
":allocator",
792+
":cache",
793+
":fingerprint_id",
794+
":init_once",
795+
":mutex",
796+
":operator_delete",
797+
":xnnpack_h",
798+
],
799+
)
800+
801+
xnnpack_cc_library(
802+
name = "fingerprint_check",
803+
srcs = ["src/xnnpack/fingerprint_check.c"],
804+
deps = [
805+
":fingerprint_cache",
806+
":fingerprint_id",
807+
":operators",
808+
":xnnpack_h",
809+
],
810+
)
811+
812+
xnnpack_cc_library(
813+
name = "operator_delete",
814+
srcs = ["src/operator-delete.c"],
815+
deps = [
816+
":allocator",
817+
":logging",
818+
":operator_h",
819+
":operator_utils",
820+
":params",
821+
":xnnpack_h",
822+
],
823+
)
824+
776825
xnnpack_cc_library(
777826
name = "operators",
778827
srcs = OPERATOR_SRCS,
@@ -804,6 +853,7 @@ xnnpack_cc_library(
804853
":microparams_init",
805854
":node_type",
806855
":normalization",
856+
":operator_delete",
807857
":operator_type",
808858
":operator_utils",
809859
":pack_lh",
@@ -932,6 +982,7 @@ xnnpack_cc_library(
932982
"//:allocator",
933983
"//:build_identifier", # build_cleaner: keep
934984
"//:common", # build_cleaner: keep
985+
"//:fingerprint_id",
935986
"//:init_once",
936987
"//:logging",
937988
"//:math", # build_cleaner: keep

CMakeLists.txt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,10 @@ 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-id OBJECT src/operators/fingerprint_id.c)
944+
ADD_LIBRARY(xnnpack-fingerprint-cache OBJECT src/operators/fingerprint_cache.c)
945+
ADD_LIBRARY(xnnpack-fingerprint-check OBJECT src/xnnpack/fingerprint_check.c)
942946
ADD_LIBRARY(xnnpack-memory OBJECT src/memory.c)
943947
ADD_LIBRARY(xnnpack-microkernel-utils OBJECT src/microkernel-utils.c)
944948
ADD_LIBRARY(xnnpack-mutex OBJECT src/mutex.c)
@@ -962,21 +966,25 @@ IF(XNNPACK_BUILD_LIBRARY)
962966
TARGET_LINK_LIBRARIES(xnnpack-allocator PRIVATE xnnpack-base xnnpack-logging)
963967
TARGET_LINK_LIBRARIES(xnnpack-cache PRIVATE xnnpack-base xnnpack-logging)
964968
TARGET_LINK_LIBRARIES(xnnpack-datatype PRIVATE xnnpack-base)
969+
TARGET_LINK_LIBRARIES(xnnpack-operator-delete PRIVATE xnnpack-base xnnpack-logging)
965970
TARGET_LINK_LIBRARIES(xnnpack-memory PRIVATE xnnpack-base xnnpack-logging)
966971
TARGET_LINK_LIBRARIES(xnnpack-microkernel-utils PRIVATE xnnpack-base xnnpack-hardware-config xnnpack-logging)
967972
TARGET_LINK_LIBRARIES(xnnpack-mutex PRIVATE xnnpack-base xnnpack-logging)
968973
TARGET_LINK_LIBRARIES(xnnpack-operators PRIVATE xnnpack-base xnnpack-allocator xnnpack-indirection
969974
xnnpack-logging xnnpack-microkernel-utils xnnpack-normalization xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing
970975
xnnpack-reference-ukernels xnnpack-datatype)
976+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-cache PRIVATE xnnpack-base xnnpack-allocator xnnpack-cache xnnpack-mutex xnnpack-operator-delete)
977+
TARGET_LINK_LIBRARIES(xnnpack-fingerprint-check PRIVATE xnnpack-base xnnpack-fingerprint-cache xnnpack-operators)
971978
TARGET_LINK_LIBRARIES(xnnpack-operator-run PRIVATE xnnpack-base xnnpack-logging)
972979
TARGET_LINK_LIBRARIES(xnnpack-operator-utils PRIVATE xnnpack-base xnnpack-logging)
973980
TARGET_LINK_LIBRARIES(xnnpack-reference-ukernels PRIVATE xnnpack-base xnnpack-datatype)
974981
TARGET_LINK_LIBRARIES(xnnpack-subgraph PRIVATE xnnpack-base xnnpack-allocator xnnpack-logging xnnpack-memory xnnpack-mutex xnnpack-operators xnnpack-operator-run xnnpack-datatype)
975982
TARGET_LINK_LIBRARIES(XNNPACK PRIVATE xnnpack-base xnnpack-allocator xnnpack-cache
976983
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)
984+
xnnpack-mutex xnnpack-normalization xnnpack-operators xnnpack-operator-run
985+
xnnpack-operator-utils xnnpack-pack-lh xnnpack-packing xnnpack-fingerprint-id xnnpack-fingerprint-cache xnnpack-microkernels-prod
986+
xnnpack-subgraph xnnpack-datatype xnnpack-reference-ukernels)
987+
TARGET_LINK_LIBRARIES(XNNPACK PUBLIC pthreadpool xnnpack-logging xnnpack-fingerprint-check)
980988
SET_TARGET_PROPERTIES(XNNPACK PROPERTIES C_EXTENSIONS YES)
981989
ENDIF()
982990
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: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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/allocator.h"
17+
#include "src/xnnpack/cache.h"
18+
#include "src/xnnpack/init-once.h"
19+
#include "src/xnnpack/mutex.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+
// The context for an XNNPack weight cache provider that we pass to operator
69+
// `create` functions when we want to fingerprint them.
70+
struct fingerprint_cache_context {
71+
void* buffer;
72+
size_t bytes;
73+
uint32_t hash;
74+
};
75+
76+
static size_t fingerprint_cache_look_up(
77+
void* context, const struct xnn_weights_cache_look_up_key* cache_key) {
78+
return XNN_CACHE_NOT_FOUND;
79+
}
80+
81+
static void* fingerprint_cache_reserve_space(void* const context, size_t n) {
82+
struct fingerprint_cache_context* const ctx = context;
83+
assert(ctx);
84+
if (ctx->buffer && ctx->bytes < n) {
85+
xnn_release_simd_memory(ctx->buffer);
86+
ctx->buffer = NULL;
87+
}
88+
if (ctx->buffer == NULL) {
89+
ctx->buffer = xnn_allocate_simd_memory(n);
90+
ctx->bytes = ctx->buffer ? n : 0;
91+
}
92+
return ctx->buffer;
93+
}
94+
95+
static size_t fingerprint_cache_look_up_or_insert(
96+
void* context, const struct xnn_weights_cache_look_up_key* cache_key,
97+
void* ptr, size_t size) {
98+
assert(context);
99+
struct fingerprint_cache_context* const ctx = context;
100+
ctx->hash = murmur_hash3(ptr, size, /*seed=*/ctx->hash);
101+
return 0;
102+
}
103+
104+
static bool fingerprint_cache_is_finalized(void* context) { return false; }
105+
106+
static void* fingerprint_cache_offset_to_addr(void* context, size_t offset) {
107+
assert(context);
108+
struct fingerprint_cache_context* const ctx = context;
109+
return ctx->buffer;
110+
}
111+
112+
static enum xnn_status fingerprint_cache_delete_cache(void* context) {
113+
struct fingerprint_cache_context* const ctx = context;
114+
if (ctx) {
115+
if (ctx->buffer) {
116+
xnn_release_simd_memory(ctx->buffer);
117+
}
118+
*ctx = (struct fingerprint_cache_context){0};
119+
}
120+
return xnn_status_success;
121+
}
122+
123+
struct fingerprint_context create_fingerprint_context(
124+
const enum xnn_fingerprint_id fingerprint_id) {
125+
struct fingerprint_context context = {
126+
.status = xnn_status_uninitialized,
127+
.fingerprint_id = fingerprint_id,
128+
.cache =
129+
(struct xnn_weights_cache_provider){
130+
.context = NULL,
131+
.look_up = fingerprint_cache_look_up,
132+
.reserve_space = fingerprint_cache_reserve_space,
133+
.look_up_or_insert = fingerprint_cache_look_up_or_insert,
134+
.is_finalized = fingerprint_cache_is_finalized,
135+
.offset_to_addr = fingerprint_cache_offset_to_addr,
136+
.delete_cache = fingerprint_cache_delete_cache},
137+
.op = NULL,
138+
};
139+
if (context.fingerprint_id == xnn_fingerprint_id_unknown) {
140+
context.status = xnn_status_unsupported_parameter;
141+
} else if (xnn_get_fingerprint(context.fingerprint_id)) {
142+
context.status = xnn_status_success;
143+
} else {
144+
// Do this after the checks to avoid a memory allocation when unnecessary.
145+
context.cache.context = xnn_allocate_zero_memory(sizeof(struct fingerprint_cache_context));
146+
}
147+
return context;
148+
}
149+
150+
static void free_fingerprint_cache_provider(
151+
struct xnn_weights_cache_provider* const provider) {
152+
if (provider) {
153+
provider->delete_cache(provider->context);
154+
if (provider->context) {
155+
xnn_release_memory(provider->context);
156+
}
157+
}
158+
}
159+
160+
void finalize_fingerprint_context(struct fingerprint_context* const context) {
161+
assert(context);
162+
if (context->status == xnn_status_uninitialized) {
163+
xnn_set_fingerprint((struct xnn_fingerprint){
164+
.id = context->fingerprint_id,
165+
.value = fingerprint_cache_get_fingerprint(&context->cache)});
166+
}
167+
free_fingerprint_cache_provider(&context->cache);
168+
if (context->op) {
169+
xnn_delete_operator(context->op);
170+
}
171+
}
172+
173+
uint32_t fingerprint_cache_get_fingerprint(
174+
const struct xnn_weights_cache_provider* const provider) {
175+
assert(provider);
176+
assert(provider->context);
177+
return ((struct fingerprint_cache_context*)provider->context)->hash;
178+
}

0 commit comments

Comments
 (0)