Measured on a Surface Laptop Ultra (Windows on Arm, RTX Spark): PyTorch 2.14.1+cu132 just worked on the GPU; torchaudio was the only piece that didn't, and the version number wasn't what had to match.
This is an English adaptation of an article I originally published in Japanese on Zenn: ComfyUI を Intel Arc から Arm の WSL2 へ。同じ requirements でも部品の版は揃わなかった. The measurements and conclusions are mine; I used an AI tool to help translate and restructure the text, and I checked the numbers against the original.
Short version: the same requirements.txt did not give me the same package versions, and when a package wouldn't install, the thing to check was its CUDA build, not its version number.
TL;DR
- Inside WSL2 on Windows on Arm, the official PyTorch 2.14.1+cu132 (Linux Arm build) ran ComfyUI on the GPU. Re-drawing with models already loaded (sampling + decode + save, not the end-to-end time for one image) took 21.60 s vs. 50.87 s on the old Intel Arc laptop: about 2.4× faster.
- torchaudio was the only package that didn't install. I first looked for a torchaudio numbered like torch (2.14.1); that premise was wrong. What had to match was the CUDA build: PyPI's torchaudio 2.11.0 targets CUDA 13.0, and my torch was CUDA 13.2.
- A CUDA 13.2 build on PyTorch's test index loaded the audio nodes, but it isn't a stable release, so the image environment still runs without torchaudio. (ComfyUI v0.38.0 and later don't require torchaudio at all.)
- The same commit's requirements installed different versions (numpy 1.26.4 → 2.5.2, opencv-python 4.10 → 5.0).
- Same workflow, input and seed: the preprocessing was nearly identical, but the final images had the same composition with different details. With a different GPU and torch, the same seed did not give the same image.
Scope
- What I moved: one image workflow (SDXL img2img with line-art and tile ControlNets), as-is: pinned ComfyUI commit, same custom nodes, the same 4 model files. The goal was to run what already worked in the same form, not to try a new model.
- From: a laptop with an Intel Arc 140V integrated GPU, running PyTorch's Intel GPU (XPU) build.
- To: a Surface Laptop Ultra (Windows 11 Arm64, NVIDIA RTX Spark N1X), inside WSL2. The GPU vendor and the CPU architecture both changed.
- Measured: what installed and what didn't; re-draw time for runs 2–5 on both machines; output differences (mean difference, PSNR, SSIM) for the preprocessing and the final image.
- Not measured: see "Where this stops" near the end.
- Versions and index contents are as recorded in October 2026, when I wrote the original.
This post keeps both my first attempt at the torchaudio problem and the correction.
The target environment
| PC | Surface Laptop Ultra (Windows 11 Arm64, NVIDIA RTX Spark N1X) |
| WSL2 | Ubuntu 24.04 (aarch64), Python 3.12.3 |
| Memory assigned to the GPU | 95.5GB (NVIDIA App setting) |
| ComfyUI | Pinned commit (table below); reports itself as 0.18.1 at startup |
| PyTorch | 2.14.1+cu132 (from PyTorch's CUDA 13.2 index, Linux Arm build) |
| Models | 4 files, about 9.5GiB in total (checkpoint, ControlNets, line-art extractor) |
WSL2 memory: raised to 24GB
According to Microsoft's WSL configuration docs, WSL2 gets 50% of Windows' total memory by default. This laptop assigns 95.5GB to the GPU, so Windows itself has only about 31GB, and WSL2 saw 15GB.
Image generation reads model files into CPU memory before moving them to the GPU. I estimated that peak at 8–16GB and created %USERPROFILE%\.wslconfig:
[wsl2] memory=24GB After wsl --shutdown and restarting WSL2, free -g showed 23GB total.
As it turned out, WSL2 memory use peaked at 5.5GiB during the first generation, so 24GB was more than enough. For this workflow, the default 15GB may well have been enough. I kept 24GB for larger models later.
Pin ComfyUI, then read the requirements before installing
I checked out the exact commits running on the old laptop:
| Repository | Commit | |
|---|---|---|
| ComfyUI | comfyanonymous/ComfyUI | 13917b388028aee922efce6b4714db96e8cfea36 ("Nightly Nvidia pytorch is now cu132 (#13288)") |
| Custom nodes | Fannovel16/comfyui_controlnet_aux | 95a13e2e5d8f8ae57583fbebb0be1f670889858b ("Merge pull request #615 …") |
Before installing, I checked both requirements.txt files for lines that pull from somewhere other than PyPI: git+ lines, URLs, -e, --index-url, and so on.
grep -nE 'git\+|https?://|^-e|--index-url|--extra-index-url|--find-links' requirements.txt Neither file had any.
PyTorch: the official CUDA build installed as-is
As in my initial setup of this laptop, I installed the official CUDA 13.2 build for Linux Arm:
torch 2.14.1+cu132 cuda.is_available() True NVIDIA RTX Spark N1X (6144-core Blackwell RTX GPU) get_arch_list() ['sm_80', 'sm_90', 'sm_100', 'sm_110', 'sm_120'] This GPU is compute capability 12.1, but the arch list has no sm_121; it stops at 12.0. Generation still ran on the GPU. NVIDIA's Blackwell compatibility guide says a cubin built for one compute capability runs on GPUs with the same major version and an equal or higher minor version. 12.0 code running on 12.1 is consistent with that.
torchaudio: the CUDA version didn't match, not the version number
At this commit, ComfyUI's requirements.txt lists torch, torchvision and torchaudio. Only torchaudio failed to install.
First attempt: look for the same version as torch, then drop it
torch was 2.14.1, so I looked for torchaudio 2.14.1.
- On PyTorch's CUDA 13.2 index, the newest torchaudio was a 2.2.0 aarch64 build. Nothing newer was there, for aarch64 or any other platform. Installing torch, torchvision and torchaudio together from that index stopped with
Cannot install torch and torchaudio==2.2.0 because these package versions have conflicting dependenciesandResolutionImpossible. - On PyPI, the latest was 2.11.0. No 2.14.1.
- The CUDA 13.0 and 12.8 indexes also stopped at torchaudio 2.11.0.
So I installed only torch and torchvision from the CUDA 13.2 index, and installed ComfyUI's requirements with the torchaudio line removed.
I also pinned torch and torchvision with a pip constraints file. The torch line in requirements has no version, and I was worried that dependency resolution might swap the CUDA 13.2 torch for the PyPI one. At the time I thought PyPI's Linux Arm torch was a CPU-only build. Later, reading the METADATA of PyPI's aarch64 wheel, I found it depends on CUDA 13.0. So there was no risk of ending up on a CPU build, but I still didn't want 13.2 swapped for 13.0. I never actually saw the swap happen. After pinning, torch stayed at 2.14.1+cu132. The fact that PyPI's Linux Arm builds target CUDA 13.0 comes up again below.
Roughly (not the literal commands I typed):
pip freeze | grep -E '^(torch|torchvision)==' > constraints.txt grep -v '^torchaudio' requirements.txt > requirements-noaudio.txt pip install -r requirements-noaudio.txt -c constraints.txt The only effect was that three audio node files (nodes_audio.py, nodes_lt_audio.py, nodes_audio_encoder.py) failed to load at startup. The image workflow doesn't use them.
Later: looking for the same number was the mistake
torchaudio's official installation page says:
TorchAudio 2.11 works with PyTorch 2.11 and with every future PyTorch release
I confirmed it in PyPI's METADATA as well. torchaudio 2.10.0 has Requires-Dist: torch==2.10.0, but 2.11.0 has no torch requirement. Since 2.11, torchaudio is not tied to a torch version. Searching for a torchaudio numbered 2.14.1 was the wrong premise.
PyPI's 2.11.0 installed, then failed at import
So I installed PyPI's torchaudio 2.11.0 (aarch64), in a copy of the image venv. The image venv itself was not touched.
With the constraints still in place, pip installed only torchaudio 2.11.0, and torch was not replaced. But import failed:
RuntimeError: Detected that PyTorch and TorchAudio were compiled with different CUDA versions. PyTorch has CUDA version 13.2 whereas TorchAudio has CUDA version 13.0. Please install the TorchAudio version that matches your PyTorch version. PyPI's aarch64 torchaudio is built for CUDA 13.0, and torchaudio checks its CUDA version against torch's at import (_check_cuda_version() in torchaudio/_extension/utils.py). My torch was CUDA 13.2. ComfyUI started, but the audio nodes failed with the same error, and the node count was 706 (20 of them audio), the same as with no torchaudio at all.
pip succeeding did not mean it worked. What had to match was the CUDA version, not the version number.
A CUDA 13.2 build of 2.11.0 was on the test index
At the time of writing (October 2026), the stable CUDA 13.2 index had no torchaudio 2.11. PyTorch's test index had torchaudio-2.11.0+cu132-cp312-cp312-manylinux_2_28_aarch64.whl. I installed it with --no-deps so it wouldn't touch torch. torch and torchvision stayed the same.
Roughly (not the literal command):
pip install --no-deps --index-url https://download.pytorch.org/whl/test/cu132 "torchaudio==2.11.0+cu132" torch and torchaudio __version__, and torch.cuda.is_available():
2.14.1+cu132 2.11.0+cu130 True torchaudio's __version__ says "2.11.0+cu130", but the CUDA check at import passed.
- MelSpectrogram and resample ran on the GPU.
- The "torchaudio missing" line disappeared from ComfyUI's startup log, and the audio nodes loaded without errors.
- Nodes went from 706 to 732, and audio nodes from 20 to 45 (LoadAudio, SaveAudio, VAEEncodeAudio, VAEDecodeAudio and others).
- One image from the image workflow also ran to the end in this state.
Still not in the image environment
The test index is where builds sit before a stable release. The stable CUDA 13.2 index had no 2.11 (at the time of writing), so I kept torchaudio out of the image environment. The results above are from the copied venv.
On the ComfyUI side, torchaudio has since been removed from requirements (#16457, "Remove torchaudio dependency."). The PR description says "no stable cu132 build", naming the lack of a stable CUDA 13.2 build as the reason. v0.37.0's requirements include torchaudio; v0.38.0's don't. Because I had pinned a commit, I landed on a version from before the removal.
Custom nodes: skipped a dependency I don't use
From the custom nodes' (comfyui_controlnet_aux) requirements, I left out mediapipe. It's used by the face, pose and hand nodes, which this workflow doesn't use.
On PyPI, mediapipe 0.10.33 has no Linux aarch64 wheel; the newer 1.1.1 does. I haven't checked whether this custom node pack works with 1.x. I'll check when I need it.
After installing, I confirmed with pip list that mediapipe, insightface and torchaudio, and any Intel GPU packages (names containing intel or xpu), were not installed.
onnxruntime also wasn't installed, because neither requirements file lists it. At startup there's a warning that DWPose (pose estimation) falls back to OpenCV on the CPU. This workflow doesn't use it either.
Package versions didn't match the old laptop
Even from the same commit's requirements, the installed versions didn't match the old laptop. Most requirements don't pin versions (some only set a floor, like numpy>=1.25.0), so you get whatever is newest at install time.
| Package | Old laptop (at migration) | Surface (WSL2) |
|---|---|---|
| torch | Intel GPU (XPU) build | 2.14.1+cu132 |
| torchaudio | installed | not installed (see above) |
| numpy | 1.26.4 | 2.5.2 |
| opencv-python | 4.10.0.84 | 5.0.0.93 (headless too) |
| pillow | 12.1.1 | 12.3.0 |
| transformers | 5.5 | 5.20.2 |
| onnxruntime | 1.24.4 | not installed |
| comfyui_frontend_package | 1.42.8 | 1.42.8 |
The old-laptop column is as of the migration. Since then, the old laptop moved from Python 3.12 to 3.14, numpy to 2.5.4, and opencv-python to 4.14. The output comparison below was done with these newer versions.
I didn't bring the Surface back in line with the old laptop. At migration time, numpy had crossed from 1.x to 2.x and opencv-python from 4.x to 5.x: major-version jumps. How the output changed is in "Comparing the outputs" below.
I saved the full pip freeze to a file after installing, so that if a difference shows up later I can trace which package differed.
Models: verified before placing them
I copied the 4 model files (about 9.5GiB) from the old laptop. After copying, I checked SHA256 and byte counts against the originals, then checked again with sha256sum -c after placing them inside WSL2. All 4 matched.
One of them (netG.pth, the line-art extractor) is a pickle file: the public AnimeLineArt weights that controlnet_aux uses. Before loading it, I inspected it without executing the pickle opcodes, the same way as in an earlier article (Japanese). Everything it calls was within the allowlist.
Startup and the first image
Part of the startup log:
Total VRAM 112872 MB, total RAM 23982 MB pytorch version: 2.14.1+cu132 Device: cuda:0 NVIDIA RTX Spark N1X (6144-core Blackwell RTX GPU) : cudaMallocAsync ComfyUI version: 0.18.1 112872MB of VRAM (about 110.2GiB) is more than the 95.5GB assigned in the NVIDIA App. It's the same number I noted in the initial setup article, where the total CUDA reports looked close to "dedicated" plus "shared". The 23982MB of RAM reflects the 24GB in .wslconfig.
- 706 nodes loaded, including the line-art preprocessing node (
AnimeLineArtPreprocessor). - Opening
http://127.0.0.1:<port>/in a browser on the Windows side returned HTTP 200. It arrives through WSL2's localhost forwarding; I didn't change any settings.
I submitted the workflow through the API (POST /prompt) and generated one image, the first run on migration day:
| Time | 53.2 s (first run, including model loading) |
| Peak GPU memory (nvidia-smi) | 13,579MiB |
| Peak GPU temperature | 79°C |
| Peak WSL2 memory use | 5.5GiB |
It ran on the GPU to the end. I measured speed and compared outputs against the old laptop later, under matched conditions (next two sections).
Runs 2 and later, compared with the old laptop
Same procedure on both machines:
- Restart ComfyUI and run once. This run includes model loading, so I treated it as warm-up and left it out of the comparison.
- Runs 2–5 change only the seed. Models, line-art preprocessing and VAEEncode stay cached, so only sampling, decode and save run.
- Time is the
Prompt executed in X secondsvalue from ComfyUI's log.
So this compares the time to re-draw the same picture from the second image on, not the time for one image including preprocessing.
| Run 1 (incl. loading) | Run 2 | Run 3 | Run 4 | Run 5 | Avg. runs 2–5 | |
|---|---|---|---|---|---|---|
| Old laptop (Intel Arc 140V, XPU) | 99.34 s | 51.09 | 50.89 | 51.37 | 50.12 | 50.87 s |
| Surface (RTX Spark N1X, WSL2) | 38.44 s | 21.79 | 21.57 | 21.46 | 21.59 | 21.60 s |
From run 2 on, the Surface was about 2.4× faster (50.87 ÷ 21.60).
- Peak GPU memory was about 10.6GiB on the old laptop and 13,873MiB on the Surface. The old laptop's integrated GPU has no nvidia-smi equivalent, so that number is an estimate from ComfyUI's
/system_stats(total minus minimum free). The Surface number is from nvidia-smi and, because the memory is unified, includes other processes (1,928MiB before measuring). - On the Surface, temperature peaked at 82°C (run 1) and was 75°C on run 5. I had no way to measure the old laptop's temperature, so I didn't.
- The Surface's run 1 took 38.44 s, shorter than the 53.2 s on migration day. Migration day was right after restarting WSL, with a cold disk cache; that's my reading of the difference.
Comparing the outputs
Same workflow, same input, same seed on both machines. I added one node to save the line-art preprocessor (AnimeLineArt) output, so I could compare the preprocessing result and the final output separately. The old laptop ran the newer versions mentioned above (Python 3.14, numpy 2.5.4, opencv-python 4.14).
Metrics: mean per-pixel difference (0–255), PSNR and SSIM.
| Compared | Mean diff | Max diff | PSNR | SSIM |
|---|---|---|---|---|
| Line-art preprocessing: old laptop vs. Surface | 0.044 | 4 | 61.7dB | 0.9999 |
| Final output: old laptop vs. Surface | 27.8 | 255 | 15.3dB | 0.45 |
| Final output: old laptop vs. Surface (migration-day outputs) | 27.7 | 255 | 15.3dB | 0.45 |
| Final output: same Surface, different days | 1.36 | 210 | 37.3dB | — |
| Final output: same old laptop, before vs. after the version upgrade | 6.66 | 227 | 25.5dB | — |
The line-art preprocessing was nearly identical on both machines. 96% of pixels matched exactly, and the remaining differences were at most 4. The difference between opencv-python 4.14 (old laptop) and 5.0 (Surface) barely showed up in this preprocessing result.
The final outputs differed a lot. With nearly identical preprocessing, SSIM still dropped to 0.45. The difference was spread over the whole image, not one region: split into a 4×4 grid, every cell's mean difference was between 19 and 34. Comparing the migration-day outputs gave almost the same numbers.
So the difference between the two machines arises in generation (sampling), not in preprocessing. The sampler here is euler_ancestral, which adds random noise at each step. Reading ComfyUI's code, the initial noise is generated from the seed on the CPU (comfy/sample.py), so that part should be the same on both. The noise euler_ancestral adds at each step, however, comes from the random number generator on the latent's device, i.e. the GPU (comfy/k_diffusion/sampling.py). CUDA and Intel GPUs have different RNGs, so even with the same seed the per-step noise wouldn't match, and the images would drift apart along the way. That's my inference; I did not extract and compare the two machines' random sequences.
Even on the same machine, the same seed didn't give a perfect match. Surface vs. itself on different days was 37.3dB PSNR, visually almost identical. The old laptop before vs. after its version upgrade was 25.5dB. Both are far closer than the 15.3dB between the two machines.
Visually, the composition, the placement of the figures and the position of the text were the same; the details differed: colors, how the lettering broke down, the shape and number of small background objects, and how deep the dark areas went. I didn't see one as lower quality than the other.
PyTorch's reproducibility notes say completely reproducible results are not guaranteed across PyTorch releases or platforms. Different GPUs and torch builds giving different images from the same seed falls within that.
The goal was to run what already worked in the same form. Judged by "does it produce images of the same quality" rather than "does it produce the same image", the migration worked. But the assumption that the same seed gives the same image again did not hold across machines.
A newer ComfyUI goes in a separate folder and venv
I'll also try video generation on this laptop, and video models need a newer ComfyUI. For example, MiniMax H3's official tutorial asks for "ComfyUI 0.36.0 or later".
The image workflow is pinned to a commit, as above. Upgrading it could change the image results, so the newer version got its own folder and venv.
| Image | Video | |
|---|---|---|
| ComfyUI | pinned commit (0.18.1) | v0.42.4 (3f438ca69f3d0efc5f748de055daeea9ba26ee50) |
| torch | 2.14.1+cu132 | 2.14.1+cu132 |
| torchaudio | removed | not in requirements |
| Frontend | 1.42.8 | 1.55.18 |
I checked the video install's requirements for non-PyPI sources the same way. torchaudio is no longer in its requirements, so there was nothing to remove.
One thing I noticed while picking a version: GitHub's Releases page still showed v0.42.1 as "Latest", but tags from v0.42.2 on kept coming after it (up to v0.42.5 at the time of writing). The MiniMax H3 LoRA fix (#17239) is in v0.42.3. Looking only at the latest Release, you can miss fixes that are already tagged, so I picked from the tag list.
Video test results are out of scope here.
Where this stops
- The effect of the numpy version. opencv-python 4.x vs. 5.x made almost no difference to preprocessing, but I couldn't check numpy 1.26 vs. 2.5. By the time I compared, the old laptop was already on numpy 2.5.4, and no preprocessing output from 1.26.4 remained.
- The mechanism that splits the outputs. I read in the code that the per-step noise comes from the GPU's RNG. I didn't compare the actual random sequences.
- Time for one image including preprocessing. I compared only the cached re-draw time (sampling, decode, save).
- The old laptop's GPU temperature. No way to measure it.
- Workflows that actually handle audio. With the test-index torchaudio, I confirmed only that the audio nodes load and that MelSpectrogram and resample run on the GPU. I didn't run any workflow that generates audio or reads/writes audio files.
- Pairing with a CUDA 13.0 torch. PyPI's torchaudio 2.11.0 targets CUDA 13.0, so with a CUDA 13.0 torch, the PyPI build should pass the import check. That's an inference; I didn't try it.
- Whether the custom nodes that use mediapipe (face, pose, hand) work on Arm Linux.
Takeaways
- Even on Arm Windows, inside WSL2, ComfyUI ran on the GPU with the official CUDA build of PyTorch. With models loaded, re-drawing took 21.60 s, about 2.4× faster than the old laptop (50.87 s).
- torchaudio was the only package that didn't install. I dropped it after failing to find one numbered like torch, but torchaudio 2.11 doesn't pin a torch version. On the other hand, PyPI's 2.11.0 targets CUDA 13.0 and failed at import with a CUDA 13.2 torch. When adding a package, don't look for the same number as torch; look at its dependency metadata and its CUDA version. Something missing from the stable index may be on the test index, but it isn't a stable release, so I didn't put it in the image environment. ComfyUI v0.38.0 and later don't require torchaudio at all.
- The same commit's requirements don't give you the same package versions. Keep a
pip freeze. - After migrating, compare preprocessing and generation separately. Here, preprocessing was nearly identical and the images split during generation. With a different GPU and torch, the same seed did not give the same image. Composition and placement matched, and I saw no quality difference.
- WSL2's memory defaults to half of Windows'. On a PC that assigns a lot to the GPU, that gets small; set it in
.wslconfig. - I kept the pinned version and the newer version in separate folders and venvs. When picking a newer version, look at the tag list, not just the latest Release.
Update note (Nov 2026): Since I wrote the original, PyTorch's stable CUDA 13.2 index has added torchaudio 2.11.1+cu132 for Linux aarch64, so the "not on the stable index" reason above no longer holds. I have not installed or tested it on the machine yet; everything above is the October 2026 record (torch 2.14.1+cu132, pinned ComfyUI 0.18.1). ComfyUI's current requirements (v0.45.0) still don't include torchaudio.
ComfyUI is licensed under GPL-3.0, and comfyui_controlnet_aux under Apache-2.0.
About this series
This is part of my series on the Surface Laptop Ultra (Windows on Arm with an NVIDIA GPU). Related articles:
- Initial setup: on Arm Windows, check whether each tool can use the GPU before installing it (Japanese)
- Ollama vs. llama.cpp on the Same GGUF: The 15% Speed Gap Was the Defaults, Not the Engine (English)
The original of this article: ComfyUI を Intel Arc から Arm の WSL2 へ。同じ requirements でも部品の版は揃わなかった.


