Skip to content

Optimize Windows ARM64 MSVC kernels and enhance cpuinfo ISA detection - #379

Open
pdeep854 wants to merge 1 commit into
google:masterfrom
pdeep854:arm64-port
Open

Optimize Windows ARM64 MSVC kernels and enhance cpuinfo ISA detection#379
pdeep854 wants to merge 1 commit into
google:masterfrom
pdeep854:arm64-port

Conversation

@pdeep854

@pdeep854 pdeep854 commented Aug 26, 2026

Copy link
Copy Markdown

Summary

Optimize ruy to build and run natively on Windows ARM64 under MSVC. The existing GAS inline assembly kernels are skipped on MSVC via !defined(_MSC_VER) guards; a full set of replacement kernels using NEON intrinsics (<arm_neon.h>) is appended to kernel_arm64.cc inside an #if defined(_MSC_VER) && defined(_M_ARM64) block. In addition to the standard float and int8 paths, this PR adds two new mixed-precision kernels (i8×i16 and i16×i8 → i16) that have no upstream equivalent.

This PR also bumps the cpuinfo submodule to upstream commit 66ee79c, adding full Windows ARM64 ISA detection support, including SVE/SVE2, FP16, DotProd, BF16, and I8MM capabilities, along with updated chip registry support.

All 33 existing tests pass; all 8 benchmark configurations show competitive kNeon and kNeonDotprod throughput.

Benchmark Environment

Benchmark measurements were collected on the following system:

  • Processor: Snapdragon® X Elite (X1E78100)
  • CPU: Qualcomm® Oryon™ CPU
  • Frequency: 3417 MHz
  • Cores: 12
  • Logical Processors: 12
Benchmark Results

Float GEMM

Workload Size (K) StandardCpp Neon NeonDotprod Reference
f32×f32→f32×f32 (float GEMM) 16 1.507 22.31 22.15 1.503
f32×f32→f32×f32 (float GEMM) 32 1.771 52.92 52.21 1.577
f32×f32→f32×f32 (float GEMM) 64 1.753 76.76 77.73 1.522
f32×f32→f32×f32 (float GEMM) 128 1.889 87.48 88.16 1.583
f32×f32→f32×f32 (float GEMM) 256 1.954 95.15 95.00 1.615
f32×f32→f32×f32 (float GEMM) 512 1.957 97.64 97.83 1.608
f32×f32→f32×f32 (float GEMM) 1024 1.993 99.87 99.31 1.621

Int8 Quantized GEMM

Workload Size (K) StandardCpp Neon NeonDotprod Reference
i8×i8→i32→u8 16 1.218 7.3 168.69 1.691
i8×i8→i32→u8 32 1.469 16.3 120.85 1.917
i8×i8→i32→u8 64 1.507 30.6 241.56 1.835
i8×i8→i32→u8 128 1.618 50.4 177.75 2.026
i8×i8→i32→u8 256 1.691 74.1 313.14 2.115
i8×i8→i32→u8 512 1.732 96.9 199.9 2.155
i8×i8→i32→u8 1024 1.760 112.3 268.1 2.177

i8×i8→i32→i8

Workload Size (K) StandardCpp Neon NeonDotprod Reference
i8×i8→i32→i8 16 1.221 7.3 70.86 1.686
i8×i8→i32→i8 32 1.485 16.3 920.56 1.928
i8×i8→i32→i8 64 1.531 31.1 141.99 1.882
i8×i8→i32→i8 128 1.662 51.6 279.27 2.031
i8×i8→i32→i8 256 1.714 75.0 131.8 2.112
i8×i8→i32→i8 512 1.750 97.3 198.1 2.153
i8×i8→i32→i8 1024 1.761 113.0 269.0 2.167

i8×i8→i32→i32

Workload Size (K) StandardCpp Neon NeonDotprod Reference
i8×i8→i32→i32 16 1.327 16.4 126.5 1.972
i8×i8→i32→i32 32 1.561 40.0 998.1 2.086
i8×i8→i32→i32 64 1.597 71.9 196.9 1.979
i8×i8→i32→i32 128 1.684 96.6 276.6 2.072
i8×i8→i32→i32 256 1.730 113.3 336.0 2.130
i8×i8→i32→i32 512 1.738 122.0 365.0 2.179
i8×i8→i32→i32 1024 1.761 128.8 385.9 2.172

Unsigned Quantized GEMM

u8×u8→i32→u8

Workload Size (K) StandardCpp Neon NeonDotprod Reference
u8×u8→i32→u8 16 1.218 7.3 158.65 1.687
u8×u8→i32→u8 32 1.477 16.2 20.69 1.926
u8×u8→i32→u8 64 1.556 31.3 194.24 1.881
u8×u8→i32→u8 128 1.659 51.1 57.89 2.061
u8×u8→i32→u8 256 1.741 76.3 132.72 2.139
u8×u8→i32→u8 512 1.740 96.4 199.62 2.108
u8×u8→i32→u8 1024 1.750 113.7 269.9 2.180

u8×u8→i32→i16

Workload Size (K) StandardCpp Neon NeonDotprod Reference
u8×u8→i32→i16 16 1.222 7.7 88.54 1.706
u8×u8→i32→i16 32 1.472 17.4 820.89 1.928
u8×u8→i32→i16 64 1.553 32.7 142.4 1.813
u8×u8→i32→i16 128 1.632 52.4 978.52 1.964
u8×u8→i32→i16 256 1.698 77.2 131.72 2.077
u8×u8→i32→i16 512 1.727 98.4 198.42 2.112
u8×u8→i32→i16 1024 1.756 113.6 265.8 2.132

Mixed Precision GEMM (New Kernels)

i8×i16→i32→i16

Workload Size (K) StandardCpp Neon NeonDotprod Reference
i8×i16→i32→i16 (new mixed precision) 16 1.221 7.1 947.18 1.695
i8×i16→i32→i16 (new mixed precision) 32 1.499 15.2 15.18 1.947
i8×i16→i32→i16 (new mixed precision) 64 1.534 25.5 25.54 1.870
i8×i16→i32→i16 (new mixed precision) 128 1.675 37.2 37.38 2.045
i8×i16→i32→i16 (new mixed precision) 256 1.737 47.1 47.31 2.125
i8×i16→i32→i16 (new mixed precision) 512 1.776 54.8 54.69 2.149
i8×i16→i32→i16 (new mixed precision) 1024 1.790 59.1 59.14 2.181

i16×i8→i32→i16

Workload Size (K) StandardCpp Neon NeonDotprod Reference
i16×i8→i32→i16 (new mixed precision) 16 1.222 7.2 20.72 1.702
i16×i8→i32→i16 (new mixed precision) 32 1.502 15.3 15.41 1.953
i16×i8→i32→i16 (new mixed precision) 64 1.526 25.6 25.75 1.897
i16×i8→i32→i16 (new mixed precision) 128 1.658 37.1 38.10 2.013
i16×i8→i32→i16 (new mixed precision) 256 1.747 46.4 46.59 2.105
i16×i8→i32→i16 (new mixed precision) 512 1.769 54.3 54.47 2.153
i16×i8→i32→i16 (new mixed precision) 1024 1.769 58.7 58.85 2.162

Build system:
- ruy/CMakeLists.txt: restrict /arch:AVX/AVX2/AVX512 flags to x86 MSVC
  targets; MSVC ARM64 kernels are compiled as part of kernel_arm64.cc
  (no separate file needed)

Platform detection (ruy/platform.h):
- Add _M_ARM64/_M_ARM detection alongside GCC __aarch64__/__arm__ macros
- Add _M_ARM64 to NEON detection (mandatory on AArch64, MSVC omits __ARM_NEON__)
- Add __builtin_expect no-op shim for MSVC (single canonical location)

GAS assembly guards:
- ruy/kernel_arm64.cc, kernel_arm32.cc: add !defined(_MSC_VER) to top-level
  #if guards to skip AT&T-style inline asm on MSVC
- ruy/pack_arm.cc: same guard for all 5 asm volatile pack function blocks
- ruy/kernel_arm.h, pack_arm.h: add !defined(_MSC_VER) to all asm-based
  function declarations and PackImpl/Kernel template specialization guards

Kernel params (ruy/kernel_common.h):
- Fix rhs_stride narrowing: add static_cast<int32_t> to suppress C4267
  (size_t * int assigned to int32_t member)
- Add MakeKernelParams8bitMixed(): variant of MakeKernelParams8bit for
  int16_t LHS (i16 x i8 case); stores lhs_base_ptr as reinterpret-cast
  int8_t* and lhs_stride in int16 elements (kernel multiplies by 2)

MSVC ARM64 NEON intrinsic kernels (ruy/kernel_arm64.cc, appended):
- KernelFloatNeon/A55ish/X1: 8x8 float32 GEMM using NEON FMA intrinsics
- Kernel8bitNeon/A55ish/1Col: 4x4 int8 GEMM using vmull/vmlal intrinsics
- Kernel8bitNeonDotprod/A55ish/X1/1Col: 8x8 int8 GEMM using vdotq_laneq_s32
- Pack8bitColMajorForNeon/A55ish: col-major int8 packing with vtrnq_s32
- Pack8bitColMajorForNeonDotprod/A55ish: col-major int8 packing for SDOT layout
- Pack8bitRowMajorForNeonDotprod: row-major int8 packing for kNeonDotprod
- PackFloatColMajorForNeon/A55ish: col-major float32 packing
- Kernel8bitNeonMixedInt16Rhs/Lhs: new mixed-precision i8xi16 and i16xi8
  kernels (tile 4x4, depth step 8); use RUY_MIX_MAC (vmull_s16 low +
  vmlal_high_s16 high) for 8 int16 products per accumulator step, with
  vmovl_s8 sign-extension for whichever operand is int8

Kernel dispatch (ruy/kernel_arm.h):
- Add MSVC ARM64 Kernel<> specializations for kNeon and kNeonDotprod
  (int8 and float variants) inside #if defined(_MSC_VER) && defined(_M_ARM64)
- Add mixed-precision Kernel<kNeon, i8, i16, i32, i16> and
  Kernel<kNeon, i16, i8, i32, i16> specializations (layout kColMajor/8/4);
  kNeonDotprod inherits both via RUY_INHERIT_KERNEL (no dotprod for int16)

Pack dispatch (ruy/pack_arm.h):
- Add MSVC ARM64 PackImpl<> specializations calling intrinsic pack functions
- Add mixed-precision PackImpl<kNeon, kColMajor/8/4, int16_t, int16_t> and
  PackImpl<kNeon, kColMajor/8/4, int8_t, int8_t> specializations; int16
  variant packs 4 cols x 8 depths = 64 bytes/block via vtrnq_s16/vtrnq_s32;
  int8 variant packs 4 cols x 8 depths = 32 bytes/block via vtrnq_s8/vtrn1q_s32

Test infrastructure (ruy/test.h):
- Use VirtualAlloc on Windows to give each SeparateMappingAllocator
  allocation a fresh virtual address, preventing stale PrepackedCache hits
  (Windows new[] reuses addresses; cache key is the source data pointer)

cpuinfo submodule:
- Bump from 082deff to upstream main 66ee79c, which includes full Windows
  ARM64 ISA detection (SVE/SVE2, fp16, dotprod, bf16, i8mm), chip registry
  lookup, init-by-logical-sys-info fallback, and fp16arith fallback path
@pdeep854 pdeep854 changed the title Enable Windows ARM64 MSVC support with NEON kernels and cpuinfo ISA detection updates Optimize Windows ARM64 MSVC kernels and enhance cpuinfo ISA detection Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant