Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7b59eda
Add binary cache
pfultz2 Jul 29, 2026
3242c99
Improve getting compiler version
pfultz2 Jul 29, 2026
2fdaaec
Simplify
pfultz2 Jul 29, 2026
f5bdcab
Remove locks
pfultz2 Jul 29, 2026
1dd3519
Add missing files
pfultz2 Jul 31, 2026
9a80988
Update license
pfultz2 Jul 31, 2026
bc80efa
Merge
pfultz2 Aug 7, 2026
625ad00
Cleanup binary settings
pfultz2 Aug 7, 2026
148a1cd
Simplify
pfultz2 Aug 7, 2026
de1b451
Merge
pfultz2 Aug 14, 2026
1bde05c
Add license
pfultz2 Aug 14, 2026
735d946
Fix cppcheck
pfultz2 Aug 15, 2026
7a5b11a
Hash all libraries with rocMLIR
pfultz2 Aug 15, 2026
53c0e2c
Use case-insensitive match
pfultz2 Aug 15, 2026
754a4a4
Add internal flags to the digest
pfultz2 Aug 26, 2026
4d5803d
Merge
pfultz2 Aug 26, 2026
e751586
Merge branch 'develop' into binary-cache
pfultz2 Aug 26, 2026
8d4da92
Add back compiler flags
pfultz2 Aug 26, 2026
9438c27
Merge branch 'binary-cache' of github.com:ROCm/AMDMIGraphX into binar…
pfultz2 Aug 26, 2026
74d95f2
Update key for internal compiler options
pfultz2 Aug 26, 2026
418b43f
Add unit tests for in-memory cache
pfultz2 Aug 26, 2026
a134d9e
Refactor compile_ops
pfultz2 Aug 27, 2026
cac0edd
Skip tasks that dont need compile
pfultz2 Aug 27, 2026
aacdc97
Seperate the loops
pfultz2 Aug 27, 2026
4ada6a4
Get rid of extra vector
pfultz2 Aug 27, 2026
d58f5ea
Remove error marshalling
pfultz2 Aug 27, 2026
fa41b24
Remove the reused flag
pfultz2 Aug 27, 2026
579920b
Simplify
pfultz2 Aug 27, 2026
7ad700a
Use tmp_dir
pfultz2 Aug 27, 2026
20409d6
Merge
pfultz2 Aug 27, 2026
4f65f3c
Cleanup comments
pfultz2 Aug 27, 2026
263d34e
Add changelog entry
pfultz2 Aug 27, 2026
3e6d61b
Sql backend implmementation for binary cache; Added common interface …
pnikolic-amd Sep 2, 2026
0919fc3
Add hpp file
pnikolic-amd Sep 2, 2026
eb7713f
Simplify skill
pnikolic-amd Sep 2, 2026
40e7437
Remove cross test between file and sql
pnikolic-amd Sep 2, 2026
264298f
Remove stamp
pfultz2 Sep 4, 2026
b2f4629
Switch to version id
pfultz2 Sep 4, 2026
4e6c258
Merge remote-tracking branch 'origin/binary-cache' into binary-cache-…
pnikolic-amd Sep 25, 2026
cdb51ea
Address review feedback on SQLite binary cache backend
pnikolic-amd Sep 25, 2026
58ab7b1
Add tests
pnikolic-amd Sep 25, 2026
d708b88
migraphx simplify
pnikolic-amd Sep 25, 2026
e1d5f80
CI tidy fixes
pnikolic-amd Sep 25, 2026
cc95ef4
Merge
pfultz2 Sep 26, 2026
389de61
Format
pfultz2 Sep 26, 2026
c48eb6e
Fix tidy warning
pfultz2 Sep 28, 2026
c465a68
Merge
pfultz2 Sep 28, 2026
5864c48
Merge remote-tracking branch 'origin/binary-cache' into binary-cache-…
pnikolic-amd Sep 30, 2026
58f53b0
Update tools/include/gpu/binary_cache_backend.hpp
pnikolic-amd Sep 30, 2026
bd7f4a6
Update tools/include/gpu/binary_cache_backend.hpp
pnikolic-amd Sep 30, 2026
dd24048
- Backend load/store now take the key and return or accept
pnikolic-amd Oct 1, 2026
2177966
sqlite: reject empty statements at runtime; a failed binary cache sto…
pnikolic-amd Oct 1, 2026
342d806
Simplify tool
pnikolic-amd Oct 1, 2026
a641728
Misspell resolve
pnikolic-amd Oct 1, 2026
dfc3bb5
Merge branch 'develop' into binary-cache-sql-backend
pnikolic-amd Oct 7, 2026
cead10e
Fix test_gpu_binary_cache build with std::experimental::filesystem (S…
pnikolic-amd Oct 7, 2026
ae721a9
Simplify
pnikolic-amd Oct 7, 2026
22888ae
Merge branch 'develop' into binary-cache-sql-backend
pnikolic-amd Oct 8, 2026
2bd99b5
Add sqlite tests
pnikolic-amd Oct 8, 2026
2016267
Fix mlir keys
pnikolic-amd Oct 9, 2026
33c641c
Simplify
pnikolic-amd Oct 9, 2026
180779d
Merge branch 'develop' into binary-cache-sql-backend
pnikolic-amd Oct 9, 2026
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Full documentation for MIGraphX is available at

### Added

* Added a binary cache for compiled GPU kernels: identical kernels within a model compile once, and setting the `MIGRAPHX_BINARY_CACHE` environment variable (or the `binary_cache` backend option) also persists them on disk so later compiles of the same kernels skip compilation entirely; a `binary_cache_verify` backend option recompiles reused kernels and fails if they differ.
* Added a binary cache for compiled GPU kernels: identical kernels within a model compile once, and setting the `MIGRAPHX_BINARY_CACHE` environment variable (or the `binary_cache` backend option) also persists them on disk so later compiles of the same kernels skip compilation entirely, in a directory or, for a path ending in `.db` or `.sqlite`, in a single SQLite database; a `binary_cache_verify` backend option recompiles reused kernels and fails if they differ.
* Optimized GridSample by adding a GPU JIT kernel for the ONNX `nearest`, `linear`/`bilinear`, and `cubic`/`bicubic` modes, replacing the `concat`+`gathernd` decomposition in the ONNX parser for those modes (#5139).

### Changed
Expand Down
24 changes: 24 additions & 0 deletions docs/dev/triage-migraphx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,30 @@ directories for builds you no longer use:
ls $HOME/.cache/migraphx # directories are named after the build that wrote them
rm -r $HOME/.cache/migraphx/v1-hip22.0.*

A path ending in ``.db`` or ``.sqlite`` keeps the cache in a single SQLite database instead of a
directory, which is easier to copy between machines. Like a directory cache it can be used by
several processes at once, and missing parent directories are created:

.. code-block:: bash

export MIGRAPHX_BINARY_CACHE=$HOME/.cache/migraphx/kernels.db

Every row records the full version id of the build that wrote it in the ``version`` column,
alongside the operator name, problem and solution, so the database can be inspected and pruned
with the ``sqlite3`` shell. Deleting rows does not shrink the file until it is vacuumed:

.. code-block:: bash

sqlite3 $HOME/.cache/migraphx/kernels.db \
"SELECT version, op_name, count(*) FROM cache_v1 GROUP BY version, op_name;"
sqlite3 $HOME/.cache/migraphx/kernels.db \
"DELETE FROM cache_v1 WHERE version GLOB 'v1-hip22.0.*'; VACUUM;"

A database that cannot be written to, such as a shared cache installed read-only, is still used
for lookups, and newly compiled kernels are kept in memory only. A database that cannot be
opened, or whose cache table has an unexpected layout, is skipped with a warning and the compile
proceeds without a disk cache.

The same settings are available as backend options, which take precedence over the environment
and are how tests configure the cache:

Expand Down
164 changes: 163 additions & 1 deletion src/include/migraphx/sqlite.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved.
* Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -25,23 +25,185 @@
#define MIGRAPHX_GUARD_MIGRAPHX_SQLITE_HPP

#include <migraphx/config.hpp>
#include <migraphx/errors.hpp>
#include <migraphx/filesystem.hpp>
#include <migraphx/functional.hpp>
#include <migraphx/iterator.hpp>
#include <migraphx/optional.hpp>
#include <migraphx/value.hpp>
#include <cassert>
#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {

struct sqlite_impl;
struct sqlite_stmt_impl;

/// A prepared statement. It shares ownership of the connection it was prepared on, which stays
/// open for as long as the statement exists. Copies share the same statement.
///
/// Calling it with arguments runs it: the arguments are bound to the parameters in order and the
/// result comes back as a range of rows. Since copies share one statement, only the rows of one
/// call may be alive at a time.
///
/// Not thread safe: use a statement from one thread at a time.
struct MIGRAPHX_EXPORT sqlite_stmt
{
/// The rows produced by one call of a statement, as an input range of values.
///
/// The first row is fetched when the call is made, so a statement that returns nothing, such
/// as an insert, has already run by the time the call returns, whether or not the range is
/// iterated. The statement is reset when the range is destroyed: an unfinished select holds
/// a read lock on the database until then, which would stall writers in other processes.
///
/// Refers to the statement it came from, which must outlive it.
struct rows
{
// Only ever a prvalue returned from a call, so it never needs copying or moving, and a
// copy would reset the statement out from under the original.
rows(const rows&) = delete;
rows(rows&&) = delete;
rows& operator=(const rows&) = delete;
rows& operator=(rows&&) = delete;
~rows() { stmt->reset(); }

struct iterator : iterator_operators<iterator>
{
using value_type = value;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using pointer = value*;

iterator() = default;

iterator(const rows* pparent, bool pavailable) : parent(pparent), available(pavailable)
{
}

reference operator*() const
{
assert(parent != nullptr and available);
return parent->stmt->to_value();
}

static void increment(iterator& x)
{
assert(x.parent != nullptr and x.available);
x.available = x.parent->stmt->step();
}

static bool equal(const iterator& x, const iterator& y)
{
return x.parent == y.parent and x.available == y.available;
}

private:
const rows* parent = nullptr;
bool available = false;
};

iterator begin() const { return {this, first}; }
iterator end() const { return {this, false}; }

private:
friend struct sqlite_stmt;

explicit rows(const sqlite_stmt& s) : stmt(&s)
{
// The destructor does not run when the constructor throws, so a failed first step
// resets the statement here instead.
try
{
first = stmt->step();
}
catch(...)
{
stmt->reset();
throw;
}
}

const sqlite_stmt* stmt = nullptr;
bool first = false;
};

sqlite_stmt() = default;

/// Run the statement with xs bound to its parameters in order, and return its rows.
template <class... Ts>
rows operator()(const Ts&... xs) const&
{
if(not valid())
MIGRAPHX_THROW("sqlite: calling a statement that was never prepared");
assert(sizeof...(Ts) == parameter_count());
// Anything left from the previous call, bindings or an unfinished result, goes first.
reset();
int i = 0;
each_args([&](const auto& x) { bind(++i, x); }, xs...);
return rows{*this};
}

/// The rows refer back to the statement, so a temporary one would leave them dangling.
template <class... Ts>
rows operator()(const Ts&...) const&& = delete;

bool valid() const { return impl != nullptr; }

private:
// Parameter indices are 1-based, matching sqlite's own convention.
void bind(int i, std::string_view s) const;
void bind(int i, std::int64_t x) const;
void bind(int i, const std::vector<char>& blob) const;

std::size_t parameter_count() const;

/// Step once. True when a row is available, false when the statement is done.
bool step() const;

/// Clear bindings and rewind, so the statement can be used again. Safe at any point,
/// including after step() has thrown.
void reset() const noexcept;

/// The current row as an object keyed by column name. Blobs become value::binary and SQL
/// NULL becomes a null value.
value to_value() const;

friend struct sqlite;
std::shared_ptr<sqlite_stmt_impl> impl;
};

struct MIGRAPHX_EXPORT sqlite
{
sqlite() = default;
static sqlite read(const fs::path& p);
static sqlite write(const fs::path& p);

/// Open for writing, or nullopt if the file cannot be opened or created. For callers
/// that treat an unusable database as "no cache" rather than as an error.
static optional<sqlite> try_write(const fs::path& p);

/// True when a cache path names a SQLite database, by its ".db" or ".sqlite" extension.
static bool is_database_path(const std::string& path);

std::vector<std::unordered_map<std::string, std::string>> execute(const std::string& s);

sqlite_stmt prepare(const std::string& sql);

/// How long to wait for a lock held by another connection before failing.
void set_busy_timeout(int ms);

/// True when writes will be refused. Opening for writing still succeeds on a file the OS
/// has write-protected, in which case sqlite quietly opens it read-only; this is how to tell.
bool read_only() const;

private:
std::shared_ptr<sqlite_impl> impl;
};
Expand Down
Loading
Loading