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

Reply via email to