On 6/2/2026 8:29 PM, Jin Ma wrote:
Hi Jeff,
Thanks for the quick response and for clarifying the background.
I've been investigating some vsetvl ping-ponging cases recently,
and found two distinct scenarios:
1. Intrinsic-based libraries:
In hand-written intrinsic code (e.g., some OpenCV operators), sched1
interleaves instructions with different vtype configurations even when
no data dependency exists between them. Since vsetvl instructions have
not yet been generated at that stage, sched1 has no way to account for
the vsetvl switching cost, causing what would have been 2 vsetvl insns
in the original assembly to balloon to 5. This results in noticeable
performance regressions -- over 10% slowdown on some OpenCV operators.
Performance recovers when -fno-schedule-insns is used, which confirms
sched1 is the culprit.
Wow. What we found was that once the core aspects of vectorization were
doing things sensibly that the vsetvls weren't adding any meaningful
overhead. 10% is *way* out of what I would expect. I don't know your
uarch, but I'd bet you're blowing out a scheduler queue or something
else in the uarch design. We saw similar problems during tuning around
the Veyron V2 design, but as I said, it was more a symptom of poor code
generation than a uarch or flaw in vsetvl handling.
2. Auto-vectorization:
On some benchmarks such as 525.x264_r, we also observe excessive vsetvl
switching in hot loops. However, from my analysis, by the time sched1
runs, the instructions already have severe data dependencies, leaving
little room for rescheduling to help.
Which routine? I think we had problems with vsetvls in the SATD
routines, but once we started using strides to avoid the transpose
things cleaned up quite nicely.
For case 1, I am exploring an approach of introducing a virtual vsetvl
cost model into the scheduling logic -- essentially teaching the scheduler
to weigh vsetvl switching overhead against data dependency constraints
(WAW, etc.) when making issue decisions. Your TARGET_SCHED_REORDER hook
is a good starting point, but it only reorders within the same priority
level, which may not be sufficient when conflicting instructions happen
to have different priorities. Do you have any suggestions on a better
way to model the vsetvl switching cost earlier in the pipeline, perhaps
by injecting artificial dependencies between instructions with incompatible
vtype configurations via TARGET_SCHED_DEPENDENCIES_EVALUATION_HOOK?
My hook wasn't ever supposed to be a major improvement and I wouldn't be
at all surprised if giving it more freedom to reorder across different
priorities would help.
Keep in mind that any ping-ponging from the scheduler is most an
artifact of the scheduler trying to optimize the critical path through
each extended block. So if you add dependencies to account for
vsetvls, you're likely going to introduce data dependency stalls and the
like. That's why my hook was so conservative -- and since our design
wasn't terribly sensitive to vsetvls we never really spent time
exploring if more aggressive reordering was helpful or not.
So I would start with the relevant blocks and map out where everything
goes cycle by cycle. You'll want to pay particular attention to cases
where the scheduler selects an instruction to fire that will eventually
result in a vsetvl because it has a different vector configuration than
whatever the last vector instruction was. Then you'll have to carefully
consider if that's really a good choice in general or not.
-fsched-verbose=99 will help with this analysis. I usually record the
per-cycle issue separately on paper and refer to the dumps to understand
the state of the ready queue, instructions that are deferred to
functional or data hazards, etc etc. It's tedious, but usually
beneficial in the end.
It's also not uncommon to at least consider disabling the first
scheduler or throttling back the issue rate. You have to balance that
against the fact that the first scheduler can do some great things in
terms of forcing pseudos to have overlapping lifetimes to avoid
load->use stalls. That in turn forces the register allocator to use
different architectural registers which can be helpful depending on your
uarch. And depending on uarch you may want to look at anti-dependencies
and software register renaming.
Jeff
Jeff
For case 2, I have not yet found a good solution. It is possible that
this is simply an inherent characteristic of RVV's programming model.
Any thoughts or pointers would be greatly appreciated.
OK for the trunk.
Thanks. I've pushed this to the trunk.
Best regards,
Jin