Make MSIX a first class distribution model for models: block level dedupe means two apps shipping the same model cost one download and one copy on disk #916
Unanswered
Andrew Clinick (aclinick)
asked this question in
Feature requests
Replies: 2 comments
|
Thanks for the detailed proposal. We agree with the underlying need to avoid duplicate model downloads and storage across applications, but we don’t plan to make MSIX the solution because it is Windows-specific. Instead, we want a cross-platform shared model cache with:
This should provide the key storage and bandwidth benefits while preserving application isolation consistently across Windows, macOS, and Linux. |
0 replies
|
I am changing this to GH discussions as a feature request. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Summary
Every application that uses Foundry Local downloads and stores its own private copy of
every model it uses. For a 7B class model that is several gigabytes per application, and
nothing is shared even when the bytes are identical.
Windows already solves this problem for application payload. An MSIX package is described
by a signed
AppxBlockMap.xmlthat hashes every file in 64 KB blocks. Single instancestorage keys on those hashes, so a block that is already on the machine is stored once no
matter how many packages contain it, and deployment does not fetch a block the machine
already has. The same mechanism covers both the download and the disk.
Model weights are close to the perfect input for it: large, immutable, and byte identical
wherever they appear.
What we are asking for: that Foundry Local support and document loading a model from a
read only directory inside the application's own MSIX package. Two applications shipping
the same model would then cost one copy on disk and one download, each application would
pin the exact model version it shipped and was tested against, and uninstalling an
application would reclaim its model as ordinary package payload.
The duplication is real, and it is byte identical
Measured across every Foundry Local cache on one machine:
This is not a name heuristic. Candidates were grouped by exact byte length and then every
candidate was hashed with SHA-256, which found 55 groups of identical content and 101
redundant copies. Examples:
Every one of those is the same model, downloaded and stored once per application.
Why MSIX is the right mechanism
Shipping the model as package payload changes four things at once, and no application code
is needed for any of them:
Uninstall is listed for completeness and is covered properly in a companion issue, since it
applies to the default location regardless of how models are distributed. The rows that are
unique to packaging are the first two and the last: no other mechanism available to Foundry
Local deduplicates the download as well as the disk, and none of them lets an application
share storage without also sharing fate with whoever else is using it.
The integrity row is not hypothetical. We filed a separate issue after a model was
silently corrupted by a download that was interrupted when the machine slept: the file was
left at its full final length with the remainder reading as zeros, and nothing in the SDK
could tell. A content addressed transport cannot produce that state, because a partial
transfer does not match the block hashes and is not mistaken for a complete one.
Sharing without coupling, which a shared cache cannot offer
This is the property that makes packaging different in kind from any cache location, and
it is worth stating plainly because it is easy to miss.
Single instance storage is transparent and reference counted. Each package logically
contains its own complete copy of the model. The platform stores identical blocks once and
tracks how many packages reference them. The application does not opt in, does not
coordinate with anyone, and cannot observe the difference.
The consequences are all the ones an application actually wants:
is package payload, versioned with the application, updated only when the application
updates. Nothing can move it underneath a release that was validated against it.
uninstalling the same model cannot remove, evict, downgrade, or lock this one. The
platform reference counts the blocks, so the last reference is what frees them.
There is no shared directory left behind, no ownership question about who is allowed to
delete it, and no orphan.
Compare that with a shared cache keyed on model identity, which is the right answer during
development and the wrong one here. A shared mutable directory used by multiple shipped
applications introduces exactly the problems that packaging avoids: who is allowed to evict
an entry, what happens to application A when application B's cleanup runs, what happens
when two applications want different revisions of the same model, and what a user should do
when it is corrupt. Every one of those is a coupling between applications that were never
written with each other in mind.
MSIX gives the storage and bandwidth savings of sharing with the semantics of a private
copy. That combination is the whole argument, and it is not something Foundry Local could
reasonably build itself.
What the SDK needs to make this work
The good news is that the payload looks ready for it. We inspected a cached model
directory: it contains only immutable content, and every file was written during the
download with nothing modified since.
The mutable pieces already live above it.
foundry.modelinfo.jsonsits at thecache\modelsroot rather than inside the model folder, and logs live in a separate
logsdirectory. Sothe writable index and the immutable payload are already cleanly separated, which is the
hard part.
What we think is needed:
ModelCacheDircan already bepointed anywhere, and
AppDataDirrelocates everything, so the configuration surfacelargely exists. What is not established is whether a model actually loads when its
directory cannot be written to. We have not been able to confirm this either way, and
it is the single thing this whole request depends on. If it already works, this issue
is mostly a documentation request.
model directory during load, it needs somewhere else to go, since package install
directories are read only to the application.
the SDK at it, so that applications do not have to reverse engineer the cache layout.
IsCachedAsyncand friends report it aspresent rather than trying to download it again.
Alternatives we considered and rejected
Framework packages and optional packages. Both are the textbook Windows answers for
sharing payload between applications, and both are the wrong fit here. Optional packages
must share a publisher with their main package to go through the Store, which rules out
independent vendors sharing a model. Framework packages are cross publisher, but they are
independently versioned and independently serviced, which reintroduces the problem the
application was trying to avoid: the model can move underneath an application that was
tested against a specific one. Neither is removed cleanly when the last consuming
application is uninstalled.
Shipping the model in the application's own package avoids all of that. It uninstalls with
the application, it is reference counted by the platform, and SIS still dedupes it across
unrelated publishers because dedupe operates on block hashes, not on package identity.
Applications cleaning up after themselves. Covered in the companion issue on uninstall.
Briefly: MSIX has no custom uninstall actions by design, and Windows Installer deliberately
leaves runtime created data alone, so this does not work in practice for either packaging
model.
On the objection that the app now owns model updates
Shipping the model in the package does mean the application is responsible for updating it,
and we would argue that is a benefit rather than a cost.
An application whose behaviour depends on a specific model should be able to pin that
model and update it deliberately, as part of its own dependency graph, with its own
testing and its own release. That is how every other dependency an application ships is
handled. It matters most in exactly the settings where on-device AI is most interesting,
such as clinical or regulated software, where "the model changed underneath us" is not an
acceptable answer.
All reactions