Skip to content

Expert Caching that can boost performance, heatmap, mmap pinning based on heatmap, real time cpu to device transfer and device to cpu transfer. Fully conditional on flags and self contained to not affect execution path outside of them - #26824

Closed
miltos22 wants to merge 71 commits into
ggml-org:masterfrom
miltos22:master

Conversation

@miltos22

@miltos22 miltos22 commented Aug 10, 2026 •

Copy link
Copy Markdown

This is the successor to #26563
People seem to want full performance, and other options I had not initially included. If a reviewer wants me to remove features, or apply some more "hacky"fixes and make a smaller diff version or a cleaner commit history, I am very willing to do both

Additions:

  1. Everything is dependent on flags. The process without any of the flags active is simply a 1:1 original llama instance. I did not change defaults or make anything non opt-in
  2. 2 different configurable ways to feed the heat-map with decay, from the cold path counts or from graph readback so it also works standalone
  3. Expert-level offload granularity, so the store splits the MoE weights per expert instead of per layer
  4. Device priority stores based on heatmap, so the hottest experts go to the best device and colder ones spill down to the weaker ones
  5. Expert pinning and evicting on mmap, so the pages of stable GPU experts get dropped while the likely-next ones stay warm and evictions never refault from disk
  6. Custom ram pool with reduced ram usage when -no-mmap is run along with multiple devices, since the expert tensors get ghost buffers and only what the cpu path needs is actually held
  7. Auto-fit on new functionality (off by default, ehs -1 activates everything. Slots into existing autofit so that has to be enabled or the slots set manually)
  8. Respects -ts and splits the expert slots according to it
  9. Separate throttled pcie to pcie device queue to reduce thrashing on multi gpu, with one move per turn and per-pair scratch buffers
  10. Multiple debug dials, examples of which include printing the heatmap every time stats print
  11. May work in multiple backends (I have seen others confirm but can only verify myself on vulkan and cuda)
  12. Fused cold op path, so the cold experts are calculated in one pass instead of three separate ones
  13. Copy or move mode, since copying the experts to the gpu costs ram while moving frees the ram after a verified transfer
  14. Verified swap handshake, since every expert move is hash verified before it is routed so corruption from torn copies is not a thing
  15. Sidecar heatmap persistence, so the heatmap is saved to a file next to the model on exit and loaded on the next launch so the store starts already converged
  16. Hit-rate driven adaptive resync cadence, so the swap period grows the longer the store proves itself stable and a settled store barely re-plans
  17. Set Device GPU store and priority manually (currently follows default device priority, may add manual switch too if people want it)

Quick start:

Use -ehs N to set number of expert slots. Never use in conjunction with -ncmoe as that takes away vram for static experts. In most cases its recommended to just set fit target. Otherwise you'll have to figure out the best value to set.
copy vs move mode.
Copy mode stores all experts on system RAM and only copies them over to vram. Takes up more RAM, but is faster to heat up
Move mode only keeps 1 copy of an expert at a time, moving them between your cpu and gpu. When run with -no-mmap (you cant copy experts back to mmap), it saves ram but slower to heat up
-expert-sidecar saves your heatmap next to your model file on exit or loads a previously saved heatmap if present. Helps a lot with initial speed.
-expert-pin N. Pins the N% of hottest experts on your mmap to your ram using willneed. early testing shows the optimal value is between 30 and 60 depending on model size. You can do 100 if you want to use heatmap but the model fits so you just want to have it set all memory as willneed. However pinning on its own has shown minimal benefit especially on well optimized linux distros.

Who is this for?:

Everyone can test it, but most benefit will be visible to people with 1 gpu, who have a model they can fit at least 40% of the model in their vram. Configurations outside of that can also provide a benefit but it can be hit or miss.

Known limitations:

Multi gpu performance. I have designed a swap system that in theory should not have overhead. However, I lack proper testing apparatus so multi gpu performance may not be optimal
Back-end support: Others have reported past versions working on other back-ends too. I hope thats the case. However, i am not rich. I only have access to 2 GPUs one of which is Vulcan only and old. So i can only test on Vulcan and CUDA.
Due to differences in floating point calculations depending on where each expert is calculated, even at temp 0, when different experts are cached, the output may slightly vary. I have run extended tests and confirmed this is not silent corruption and happens on the original llama too if you change "--n-cpu-moe"
High end hardware. I do not have access to any. So its impossible to optimise. Some have reported good results, some bad. But I would need to work closer with people to understand
Under 256 token generation sessions: When llama.cpp itself (client, server, etc) has processed less than 256 tokens it may be slower than stock. If you do not close and re open the client it will quickly start outpacing it. Benchmarks that generate 128 tokens and restart the server will produce uncharacteristically bad results that are not representative of real world performance.
While -np2 works, the slowdown will be bigger than on stock. So its probably better for -np 1, about on par with -np 2, and worse after that.

Warnings:

Auto-fit. Relies on stock autofit. If you have manually set -cmoe, -ncmoe, -ngl, etc etc, it will fail and you will have to manually submit a -ehs value.
If your cpu or PCI express buss is your bottleneck, it may worsen performance
The flags equivalent of -no-mmap uses a bit more ram than no mmap on the same configuration on stock (without the flags, or the pull request) due to the need for a buffer to move experts around without overwriting each other. I designed it so it only has a buffer on the CPU side so vram usage would not increase. The increase should be minor in most cases but worth knowing about
Full model fit: If the full model fits in your vram, this will purely cause slowdowns as it adds more cpu synchronization overhead. However if only 80% of the model fits, this may be the single biggest upgrade you could find.
Performance may be worse on vulkan or other back ends.

Things I have talked about but were excluded for this commit but can plug into existing system:

Semantic permanent heat maps + prefetch. Could be added in a future commit but is too big of a feature to include along with the rest here
Would benefit the most: People who want to use models drastically larger than their combined Ram.
Next expert predictive prefetch. It can actually cause a slowdown fow smaller models but for huge ones, it can be very beneficial.
For people who really want this: I hear you, I will be researching for the best way to do this with the least rewriting of original llama code over the next month

Full technical explanation:

Coming soon, I will go to sleep now. Excuse me for any possible multi hour delays in my responses.

Benchmark methodology:

Prompt: Give me a guide on how to set up a Linux computer averaged over 3 runs of each (the deviation was within 5% so i wont be putting them individually to increase readability)
The sidecar test was run having pre warmed a sidecar with the phrase "Hello" over 128 tokens. Sidecars that were generated over longer periods will perform significantly better especially when there is topic overlap.
The only launch argument for both was -fitt 64 + -ehs -1 for mine.
Hardware: 8gb laptop 3070 + 32 GB of ram + laptop ssd. System conditions identical with all other processes ended.
I initially did multiple runs on stock, but realized it made no difference (within 1-2% regardless of token length), so i stuck with 1.

##Benchmark Results (short)

Model Size S Stock (tok/s) Request (tok/s) Ratio
Qwen3.6-35B-A3B (IQ2_M) 12 GB autofit 38.1 59.7 1.57x
Qwen3.6-35B-A3B (Q4_K_M) 21 GB autofit 32.0 47.7 1.49x
Qwen3.5-122B-A10B-REAP-30 (IQ2_M) 29 GB autofit 7.18 12.5 1.74x
Gemma-4-26B-A4B (Q5_K_S) 18 GB autofit 19.9 43.6 2.19x
Laguna-S-2.1 (IQ3_XXS) 44 GB autofit 2.01 2.05 1.02x

Benchmark results (detailed)

model config 256 768 1792 3840
Qwen3.6-35B-A3B IQ2_M stock - 38.1 - -
Qwen3.6-35B-A3B IQ2_M tier 34.8 47.4 59.5 59.7
Qwen3.6-35B-A3B IQ2_M sidecar - 44.2 - -
Qwen3.6-35B-A3B Q4_K_M stock - 32.0 - -
Qwen3.6-35B-A3B Q4_K_M tier 33.8 43.3 47.7 43.7
Qwen3.6-35B-A3B Q4_K_M sidecar - 35.8 - -
Qwen3.5-122B-A10B-REAP-30 IQ2_M stock - 7.18 - -
Qwen3.5-122B-A10B-REAP-30 IQ2_M tier 10.1 11.9 12.4 12.5
Qwen3.5-122B-A10B-REAP-30 IQ2_M sidecar - 8.71 - -
gemma-4-26B-A4B Q5_K_S stock - 19.9 - -
gemma-4-26B-A4B Q5_K_S tier - 36.2 42.0 43.6
Laguna-S-2.1 IQ3_XXS stock - 2.01 - -
Laguna-S-2.1 IQ3_XXS tier 1.66 - 2.05 -

These are not performance numbers under ideal conditions. What I did was run stock first, and others later, with clearing my rams cache fully in between. The late runs, which were always the tiered runs are thermally throttled. Under ideal conditions performance is 20% higher when not ram limited.

Note: It appears to be building normally on everything. But if someone encounters a platform specific error, I'll make sure to have it fixed by tomorrow. In the meantime you can get one of the pre made builds: https://github.com/miltos22/llama.cpp-wackMall-merge-request/actions/runs/31344712283#artifacts

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: Yes, mainly deepseek v4 flash**. I used agents to automate multi hour tests between different implementations I made to find out edge cases I outlined, and for general bug hunting/syntax, and occasionally for brainstorming or copy pasting code on mass between tests and to fix compiler crashes on a short schedule. Some comments were written by AI too, but I was not able to remember which ones as it mostly copied my own style as I asked it to make my comments shorter (Im overly verbal). All the final code has been written or verified by me, although I still consider myself someone who has a lot to learn in terms of pure coding skill, I am very good at system architecture and even made a basic working OS before and have conducted successful penetration testing both in the pre AI era.

Edit after PR closure: But this was NOT vibe coded and I am confident the results reflect that + There is an established timeline of me working on this over weeks. At most you could call it debugged/accelerated by AI

** I find the fact Deepseek V4 flash was the ai that helped me during this weirdly poetic given how a previous version of this allowed a user to get a ~50% speed increase on running deepseek v4 flash locally on his single 24 GB gpu on the old push request)

miltos22 and others added 30 commits August 2, 2026 21:29
hooks into build_moe_ffn and process_ubatch, new file
llama-expert-heatmap does the actual tracking, minimal
diff on upstream files. --expert-heat-decay and
--expert-heat-log-period for config.
heatmap only allocated when log period or hot S is set, hot store only
when hot S is set, defaults off
decay_all once per batch, not once per layer; log on crossed
boundaries instead of exact multiples

Assisted-by: Gemini 3.1 Pro
- add MUL_MAT_ID_COLD op that skips hot experts on the CPU cold path
- port cold kernel from original llama-wackMall
- add llama-expert-tier register/build hook (hot LUT remap + cold op + scale + add)
- slim layer_lut to {hot_lut, cold_mask}, drop cold_lut/hot_mask
- add static-plant and hit-rate diagnostics
- add ggml-cpu-mul-mat-id-cold.c with the cold-expert kernel
- add shared header for mmid helpers (mmid_row_mapping, incr_ptr_aligned, one_chunk)
- drop static on the two helpers now shared with stock mul_mat_id
Expert hot store is only correct on CUDA; on Vulkan it produces corrupt
output. Restrict the tiered hot store to devices named CUDA*, and add
LLAMA_EXPERT_HOT_FORCE=1 to override for testing.
--expert-hot-s N>0 auto-pushes the MoE-to-CPU tensor override (with a
warning) so hot store slot copies always read host pointers.
fit.cpp now reports how many MoE expert bytes the fitted placement leaves
on GPU; common.cpp maps that to S slots and forces all experts to CPU so
the hot store copy reads host pointers. S=0 disables the tier.
Swap a resident only when a cold expert scores >= hyst x the incumbent
(default 1.3). Add --expert-hyst/--expert-dwell, raise decay default to
0.999. Dwell counts real tokens and initial fill is eligible so the first
sync can correct drift.
sync fork with ggml-org upstream (15 commits): vulkan shaders, new model
templates, gguf reader hardening, sycl/vulkan fixes. no conflicts with
the expert hot store feature.
--expert-heat-decay help said 0.99 but the default is 0.999;
--expert-hot-s help now documents -1 = autofit from free VRAM.
@troed

troed commented Aug 10, 2026

Copy link
Copy Markdown

Built this since I think my usecase is the perfect target audience - running Ornith 1.0 35B A3B Q6_K_XL on a 5060Ti 16GB.

PP is very low, around of third of usual. TG seems to be faster, but I haven't done an exact A/B test yet. I estimate TG to be about 33% faster on my regular workloads.

I'll be happy to help out posting stats somewhere else if this PR isn't the best place to minimize noise.

@IMbackK

IMbackK commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

This type of pr is not acceptable as i violates multiple rules, for example the one change per pr rule, the no changes in multiple back ends rule, vibecodeing etc.

@IMbackK IMbackK closed this Aug 10, 2026
@voidpush

Copy link
Copy Markdown

@miltos22 See? That's why I told you to create an Issue explaining the architecture first and then link to smaller PRs sequentially.

Anyway, don't lose hope. This feature will take a long time to fully implement. A ton of feedback will be required.
The next step is to create that Issue with a link to your repo for anyone who wants to test and give feedback, and then create a small Draft PR here, with the minimal changes required to implement a basic version.

Just make it modular. Show the roadmap. Refine and test that first module thoroughly. You got this.

@miltos22

miltos22 commented Aug 10, 2026 •

Copy link
Copy Markdown
Author

@miltos22 See? That's why I told you to create an Issue explaining the architecture first and then link to smaller PRs sequentially.

Ok I get it. However I I am not sure how much time I can put into this as it has taken over my life for 3 weeks

More crucially the only parts that were "vibe coded" were the fixing compiler errors parts so I'm also very deterred by that. I did use AI to discuss about code or change the order of thing or do them more efficiently for the rest but that was mostly it. It really feels like the assumption is made due to the size of the code to me. I think anyone looking at the old pr and my constant updates can likely tell I put in a lot of work directly. And I would legitimately want to see someone attempt to vibe code something like this as with the models I have access to i feel like it's impossible and even if someone used fable it may or may not have worked but it would probably cost more than an actual computer. Perhaps it was because some initial commits felt too fast? Which was because I already had code ready and was just more or less copying it to make the commit history cleaner

@miltos22

Copy link
Copy Markdown
Author

This type of pr is not acceptable as i violates multiple rules, for example the one change per pr rule, the no changes in multiple back ends rule, vibecodeing etc.

Would you like to discuss with me how I could split up the work so it would have a chance?

@kabhinara

Copy link
Copy Markdown

Not sure if it helps, but the PR really does help boost tg/s for me, it increases performance by about 70% for me on an RTX 4070 Laptop (8GB VRAM) + 16GB DDR5 system (i7 13700HX (no AVX512))

BUT the prompt processing got divided by 3 essentially (almost 4 actually :/), so there is some sort of a drawback, perhaps I will try cranking up slots and/or batch size..

@Tha14

Tha14 commented Aug 10, 2026

Copy link
Copy Markdown

Not sure if it helps, but the PR really does help boost tg/s for me, it increases performance by about 70% for me on an RTX 4070 Laptop (8GB VRAM) + 16GB DDR5 system (i7 13700HX (no AVX512))

BUT the prompt processing got divided by 3 essentially (almost 4 actually :/), so there is some sort of a drawback, perhaps I will try cranking up slots and/or batch size..

That's the expected outcome of this. You are trading PP for TG speed.

@kabhinara

Copy link
Copy Markdown

Didn't think it would be this bad though. And I dont see why it should be this bad in the first place...

@IMbackK

IMbackK commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@miltos22 See? That's why I told you to create an Issue explaining the architecture first and then link to smaller PRs sequentially.

Anyway, don't lose hope. This feature will take a long time to fully implement. A ton of feedback will be required. The next step is to create that Issue with a link to your repo for anyone who wants to test and give feedback, and then create a small Draft PR here, with the minimal changes required to implement a basic version.

Just make it modular. Show the roadmap. Refine and test that first module thoroughly. You got this.

sounds like a solid plan

@miltos22

miltos22 commented Aug 10, 2026 •

Copy link
Copy Markdown
Author

edit: My report was prematures. When running in router mode, only the first loaded model behaves properly, every other model after that seems to run pp on cpu only. edit2: It might be when a model gets loaded as part of a decode request with a different model. I managed to break it with the first loaded model too. edit3: Ok, seems like prompt processing is ALWAYS done on cpu. (300 -> 2 tps)

Tried it with https://huggingface.co/badtheorylabs/BTL-4-Compact , and it causes pp to be done by cpu only (RTX 2070 8gig). (-ehs -1)

Another qwen 3.6 based model works fine though.

I think that was due to a workaround I had introduced earlier before I managed to make parallelization work with it and I may have forgotten to undo. I'll have a look later. It should theoretically have the capability to do it on the GPU so it should be a relatively small scale fix

However Making it work with router mode would probably be a bit more difficult as I made no consideration so far for it. So if that turns out to be easy too, it would be pure luck

@miltos22

Copy link
Copy Markdown
Author

Anyway, don't lose hope. This feature will take a long time to fully implement. A ton of feedback will be required. The next step is to create that Issue with a link to your repo for anyone who wants to test and give feedback, and then create a small Draft PR here, with the minimal changes required to implement a basic version.

I would like to do this but I am not sure how I could ever cut it down. 90% of the changes depend on the others to offer any real benefit.
The only thing that COULD be self contained and separate is the pinning + heatmap on mmap. However this has almost no real benefit in my testing on its own too.

@Tha14

Tha14 commented Aug 10, 2026

Copy link
Copy Markdown

Anyway, don't lose hope. This feature will take a long time to fully implement. A ton of feedback will be required. The next step is to create that Issue with a link to your repo for anyone who wants to test and give feedback, and then create a small Draft PR here, with the minimal changes required to implement a basic version.

I would like to do this but I am not sure how I could ever cut it down. 90% of the changes depend on the others to offer any real benefit. The only thing that COULD be self contained and separate is the pinning + heatmap on mmap. However this has almost no real benefit in my testing on its own too.

Personally I would create the issue that shows the complete implementation and it's benefits. After that you start breaking it down into parts so that they can be merged one by one. Each part does not have to explicitly provide a direct benefit other than to create the structure for the whole implementation. As far as I understand the whole purpose to doing small PRs is to be able to merge changes without creating new bugs and unintended behavior. Simply break it down. Heck, you can even use whatever LLM you want to create a plan as to which parts should be separated into their own PRs and then spend the time reviewing that instead if it helps you. There's not real point into giving up now. If you need help getting this sorted you know where to find me.

@miltos22

miltos22 commented Aug 10, 2026 •

Copy link
Copy Markdown
Author

I want to knowledge something I did wrong.

Yesterday I pushed and realized I could not sync automatically due to a conflict that arose from having applied manually the latest sync on my local PC. It was 4 AM so i just told AI to get it sorted without a second look. What it ended up doing was tracking the entire folder including 2 files, counter.md and manuallog.md

These 2 files were an issue. First of all, they were days behind the actual development and did not represent the final state of the code as they were only included in my sandbox repos and though my convoluted copy paste replace application method to the real repo, they rode along.

Second, despite the content of the files being just general architecture notes or notes from ai to ai keeping track of past bugs and times to argue about changing my implementation into something that was instantly proven to be worse, I get that including them accidentally was sloppy and gives off a terrible taste as to the overall quality of the work. So I would like to apologize for that.

I made no attempts to hide that this was largely accelerated with AI and likely it would have taken me months rather than weeks without the usage of AI both debugging and testing as disclosed.
Yet I am confident this is very far from Vibe coding or AI slop.
As originally stated I can justify and I'm responsible for 100% of the code and a large portion was directly written by me while the rest verified by me.

I removed those documents now and I am sorry. Resolving the sync conflict was something I should have checked manually rather than just ask AI to sort out.

My initial resistance to split it into multiple parts is simple. I don't think this is something that should be fully automated. And manually doing so can take days while I already felt burnt out from 16 hours a day of coding.

However I decided to commit to doing it the proper way as I believe this is something that could be very beneficial.
But I am taking a break for the res of today

@miltos22

miltos22 commented Aug 10, 2026 •

Copy link
Copy Markdown
Author

Built this since I think my usecase is the perfect target audience - running Ornith 1.0 35B A3B Q6_K_XL on a 5060Ti 16GB.

PP is very low, around of third of usual. TG seems to be faster, but I haven't done an exact A/B test yet. I estimate TG to be about 33% faster on my regular workloads.

I'll be happy to help out posting stats somewhere else if this PR isn't the best place to minimize noise.

Since it was closed, we can talk about stats and potential changes before I commit to a final roadmap and start splitting changes up in smaller parts

@simlu

simlu commented Aug 11, 2026

Copy link
Copy Markdown

@miltos22 This might be interesting for you: #24528

@miltos22

miltos22 commented Aug 11, 2026 •

Copy link
Copy Markdown
Author

@miltos22 This might be interesting for you: #24528

Will have a more detailed look but I feel just from a quick first impression like it fell on many traps I went out of my way to avoid. For instance worse performance on static experts than stock, (mine is within 10% even with heatmap disabled), cuda only, etc. However it most definitely predates mine. And I was unaware of it. So I may need to give a warning that I acknowledge it's existence. However it's clear that I am far less professional in both my presentation and the way I respond which I will also take note of

@sdroege

sdroege commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

And I was unaware of it

I linked to that in the first comment of the original PR for this :) See #26563 (comment)

@miltos22

miltos22 commented Aug 11, 2026 •

Copy link
Copy Markdown
Author

I linked to that in the first comment of the original PR for this :) See #26563 (comment)

Oh right. I will admit I confused myself thinking this was a different one you were pointing out than that comment. I know this may seem intentional but what actually happened is I simply responded first thing when I woke up so I'm a bit out of it. I even went back and checked if the number was the same and somehow I managed to missread it. I won't make a list on how our implementations different but they are only vaguely similar

@mgabor3141

Copy link
Copy Markdown

[AGENT]

@miltos22 Thanks for this work. I tested PR head 440107517 on 4× RTX 3090 with DeepSeek-V4-Flash UD-Q8_K_XL.

The build succeeds, but multi-GPU loading fails:

  • --expert-hot-s -1 refuses autofit when -ngl or --tensor-split is specified.
  • Manual --expert-hot-s 120 --expert-move-mode 2 reports a 16.6 GiB store “across 4 devices,” then attempts another 66.3 GiB allocation on CUDA0 and aborts in llama_expert_preload::begin():
cudaMalloc failed: out of memory
ggml-backend.cpp:153: GGML_ASSERT(buffer) failed

All four GPUs were visible and empty; there was no Xid or host OOM.

This looks like the multi-device store is being sized globally but allocated through CUDA0. I have this rig available and would be happy to test a fix, including P2P transfers and prefill/decode performance.

IlyaFeoktistov pushed a commit to IlyaFeoktistov/llama.cpp that referenced this pull request Aug 14, 2026
Points at expert_streaming.py and lists the 3 real commits this branch
carries on top of upstream (PR ggml-org#26824 head, gpt-oss compat, GLM-4.7-Flash
compat) -- so anyone opening this fork's README sees what changed and why
before reading upstream's own content.
@xashr

xashr commented Aug 14, 2026

Copy link
Copy Markdown

Added to performance comparison here .
=> Great TG, Very low PP

@mistenman

Copy link
Copy Markdown

This can be the next big thing! Don't give up♥️

@TacoTakumi

Copy link
Copy Markdown

I came across your PR because I was doing research on doing the same thing! I have a mixed 5 GPU rig. I'm going to dig deeper into this. Keep up the good work, It's really hard getting a PR accepted here - I've tried.

@TacoTakumi

Copy link
Copy Markdown

@miltos22 are you continuing your work in this area?

@miltos22

Copy link
Copy Markdown
Author

@miltos22 are you continuing your work in this area?

Its complicated. Im trying to experiment on and off and have a few people who have proposed to collaborate but i really need a mental health break at the moment. So, likely, soon-ish

@TacoTakumi

Copy link
Copy Markdown

All the best! I'll be keeping an eye out. I'm also digging into #27861 now.

@yuffx

yuffx commented Sep 25, 2026 •

Copy link
Copy Markdown

And I would legitimately want to see someone attempt to vibe code something like this as with the models I have access to i feel like it's impossible and even if someone used fable

I know a guy who coded an entire backend with expert caching from scratch with claude. Yes, it's totally doable, just pricey.

I suggest you to chime in into expert cache thread in discussions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CUDA Related to the CUDA backend documentation Improvements or additions to documentation ggml changes relating to the ggml tensor library for machine learning server

Projects

None yet

Development

Successfully merging this pull request may close these issues.