Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/usage/record.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,33 @@ mneme::annotate(d_output, mneme::Metadata{
});
```

For cases where the pointer you want to annotate is an interior pointer
into a larger allocation, use the sub-region annotation feature and
pass the byte extent of the logical field you want Mneme to verify:


```cpp
double* d_alias = base + offset;
std::size_t alias_bytes = count * sizeof(double);

mneme::annotate(d_alias, alias_bytes, mneme::Metadata{
.builtin = mneme::BuiltinDType::F64,
.threshold = 1e-6,
.threshold_kind = mneme::ThresholdKind::Relative,
.norm = mneme::Norm::Linf,
.tag = std::string("alias_region"),
});
```

Annotations must be applied **before** the kernel launch they should
affect. You can update the annotation on the same pointer between
launches to record different tolerance policies for different dynamic
instances of the same kernel.

Use `mneme::annotate(ptr, md)` when `ptr` is the base of the whole
allocation. Use `mneme::annotate(ptr, nbytes, md)` when `ptr` is an
interior pointer or when only a sub-region of the allocation should
carry that metadata.

For the full API reference, supported data types, threshold semantics,
and a complete example, see **[Usage → Verification](verification.md)**.
35 changes: 35 additions & 0 deletions docs/usage/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,46 @@ replayed epilogue satisfies every annotated buffer's tolerance.

---

## Region annotations

Use the region overload when the pointer to annotate is an alias or
interior pointer into a larger allocation, or when one allocation
contains multiple logical fields with different verification rules.

```cpp
double* d_alias = base + offset;
std::size_t alias_bytes = count * sizeof(double);

mneme::annotate(d_alias, alias_bytes, mneme::Metadata{
.builtin = mneme::BuiltinDType::F64,
.threshold = 1e-12,
.threshold_kind = mneme::ThresholdKind::Relative,
.norm = mneme::Norm::L2,
.tag = std::string("alias_region"),
});
```

- `mneme::annotate(ptr, nbytes, md)` applies metadata only to the byte range
`[ptr, ptr + nbytes)`.
- The full-range annotation acts as the default policy for uncovered bytes.
- Narrower region annotations override that default on covered bytes.

Constraints:
- Overlapping region annotations on the same blob are rejected.
- `nbytes` must stay within the owning allocation.
- `nbytes` must be compatible with the selected `BuiltinDType` element size.

This is the intended API for alias pointers produced by frameworks such as
MFEM on top of allocators such as Umpire.

---

## Summary

| Concept | Description |
| ------- | ----------- |
| `mneme::annotate(ptr, md)` | Attach verification metadata to a device pointer |
| `mneme::annotate(ptr, nbytes, md)` | Attach verification metadata to a byte sub-region of an owning allocation |
| `BuiltinDType` | Scalar type used to interpret buffer contents |
| `ThresholdKind` | Absolute or relative error formula |
| `Norm` | Per-element (`None`) or aggregate (`L1`, `L2`, `Linf`) comparison |
Expand Down
33 changes: 29 additions & 4 deletions include/mneme/MnemeAnnotation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
//===----------------------------------------------------------------------===//
// mneme_annotate.h - User-facing annotation API for Mneme
//
// This header declares mneme::annotate(ptr, Metadata{...}) and related types.
// This header declares mneme::annotate(ptr, Metadata{...}) and sub-region
// annotation overloads such as mneme::annotate(ptr, nbytes, Metadata{...}).
// It is intentionally *interface-only*: no lambdas / comparators yet.
//
// Example:
Expand All @@ -18,9 +19,14 @@
// You can also use the typed helper:
// mneme::annotate<double>(p, mneme::Metadata{ .threshold = 0.1 });
//
// Or annotate a sub-region explicitly:
// mneme::annotate(p, 128 * sizeof(double),
// mneme::Metadata{ .threshold = 0.1 });
//
//===----------------------------------------------------------------------===//

#include <cstdint>
#include <cstddef>
#include <optional>
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -72,6 +78,16 @@ struct Metadata {
std::optional<std::string> tag = std::nullopt;
};

inline bool operator==(const Metadata &LHS, const Metadata &RHS) {
return LHS.builtin == RHS.builtin && LHS.threshold == RHS.threshold &&
LHS.threshold_kind == RHS.threshold_kind && LHS.norm == RHS.norm &&
LHS.tag == RHS.tag;
}

inline bool operator!=(const Metadata &LHS, const Metadata &RHS) {
return !(LHS == RHS);
}

// --------- Builtin dtype mapping helpers (optional sugar) ------------------

template <class T> struct builtin_dtype_of {
Expand Down Expand Up @@ -115,17 +131,26 @@ template <> struct builtin_dtype_of<std::uint64_t> {
// Primary user API: annotate a pointer with metadata.
void annotate(const void *ptr, Metadata md);

// Convenience overload for non-const pointers.
void annotate(void *ptr, Metadata md);
// Annotate a sub-region beginning at ptr and spanning bytes bytes.
void annotate(const void *ptr, std::size_t bytes, Metadata md);

// Typed helper: sets builtin dtype automatically when T maps to a known
// BuiltinDType. If T is unknown, this will leave builtin=Unknown (still useful
// if you set dtype=Custom later).
template <class T> inline void annotate(T *ptr, Metadata md = {}) {
template <class T,
std::enable_if_t<!std::is_void_v<std::remove_cv_t<T>>, int> = 0>
inline void annotate(T *ptr, Metadata md = {}) {
// If the user didn't specify dtype explicitly, keep default Builtin.
// If they *did* specify Custom, we don't override anything here.
md.builtin = builtin_dtype_of<std::remove_cv_t<T>>::value;
annotate(static_cast<const void *>(ptr), std::move(md));
}

template <class T,
std::enable_if_t<!std::is_void_v<std::remove_cv_t<T>>, int> = 0>
inline void annotate(T *ptr, std::size_t bytes, Metadata md) {
md.builtin = builtin_dtype_of<std::remove_cv_t<T>>::value;
annotate(static_cast<const void *>(ptr), bytes, std::move(md));
}

} // namespace mneme
Loading