Description
rustc 1.81 completely replaced the implementation of slice::sort_unstable_by_key. These changes are mentioned in the release notes, and more information can be found in the corresponding PR: rust-lang/rust#124032.
The new sorting algorithm was supposed to be faster in the general case, but in some specific cases it looks like it is way slower, and this has impacted the wasmtime
crate via regalloc2
. I've experienced a 10x times slowdown when compiling some WASM modules using wasmtime
with rustc 1.81 (more details can be found in: VirusTotal/yara-x#209)
I tracked down the issue to be caused by the merge_bundles function, which uses sort_unstable_by_key
. According to my investigation, sort_unstable_by_key
is being called with slices that are mostly sorted. Apparently, two already sorted slices are being concatenated and then sorted again. This is confirmed by the following comment in the code of merge_bundles
:
// Two non-empty lists of LiveRanges: concatenate and
// sort. This is faster than a mergesort-like merge into a new
// list, empirically.
This pattern, which worked well with the previous implementation of sort_unstable_by_key
, seems to be pathologically bad for the new one. The assumption that concatenating the two lists and sorting them again is faster than merging the two sorted lists likely no longer holds true.
If you are curious about which WASM code is triggering this issue, it is an implementation of the "switch" statement in WASM using multiple nested blocks and a br_table
instruction that jumps out the block that corresponds to the input value. The overall schema is shown below.
(func $switch_example (param $x i32)
(block $default ;; Default branch
(block $case2 ;; Case 2
(block $case1 ;; Case 1
(block $case0 ;; Case 0
;; Use br_table to jump based on $x
(local.get $x) ;; Push x onto the stack
(br_table $case0 $case1 $case2 $default) ;; Jump to the right case
)
;; Code for Case 0
;; Add your case 0 logic here
(br $default) ;; Continue to the end
)
;; Code for Case 1
;; Add your case 1 logic here
(br $default) ;; Continue to the end
)
;; Code for Case 2
;; Add your case 2 logic here
(br $default) ;; Continue to the end
)
;; Default case code
)