Skip to content

powi is slow #516

@voidentente

Description

@voidentente

Premise

I was wondering whether I could replace expressions like x * x with x.powi(2) and went ahead and compared the generated SPIR-V in both cases. Turns out, Rust-GPU fails to optimize even the most obvious opportunities.

Method

I inspected the SPIR-V using https://www.khronos.org/spirv/visualizer/ and the artifact was generated in release mode using rust-gpu 08f98a7 (a commit from Mon Jan 5 12:45:27 2026 +0100), which uses Rust nightly-2025-06-30.

Case: Manual Multiply

This Rust code:

#[spirv(fragment)]
pub fn fragment(out: &mut Vec4, #[spirv(frag_coord)] frag_coord: Vec4) {
    *out = Vec4::splat(frag_coord.x * frag_coord.x);
}

Becomes the following SPIR-V:

OpCapability Shader
OpCapability VulkanMemoryModel
OpMemoryModel Logical Vulkan
OpEntryPoint Fragment %1 "fragment" %2 %3
OpExecutionMode %1 OriginUpperLeft
OpDecorate %2 BuiltIn FragCoord
OpDecorate %3 Location 0
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Output %7
%9 = OpTypePointer Input %7
%10 = OpTypeVoid
%11 = OpTypeFunction %10
%2 = OpVariable %9 Input
%3 = OpVariable %8 Output
%1 = OpFunction %10 None %11
%12 = OpLabel
%13 = OpLoad %7 %2
%14 = OpCompositeExtract %6 %13 0
%16 = OpFMul %6 %14 %14
%17 = OpCompositeConstruct %7 %16 %16 %16 %16
OpStore %3 %17
OpReturn
OpFunctionEnd

Case: Powi

The following Rust code:

#[spirv(fragment)]
pub fn fragment(out: &mut Vec4, #[spirv(frag_coord)] frag_coord: Vec4) {
    *out = Vec4::splat(frag_coord.x.powi(2));
}

Becomes the following SPIR-V:

OpCapability Shader
OpCapability VulkanMemoryModel
OpMemoryModel Logical Vulkan
OpEntryPoint Fragment %1 "fragment" %2 %3
OpExecutionMode %1 OriginUpperLeft
OpDecorate %2 BuiltIn FragCoord
OpDecorate %3 Location 0
%14 = OpTypeFloat 32
%15 = OpTypeVector %14 4
%16 = OpTypePointer Output %15
%17 = OpTypePointer Input %15
%18 = OpTypeVoid
%19 = OpTypeFunction %18
%2 = OpVariable %17 Input
%20 = OpTypeBool
%21 = OpTypeInt 32 1
%25 = OpTypeInt 32 0
%30 = OpConstant %25 0
%31 = OpConstant %25 1
%32 = OpConstant %21 1
%33 = OpUndef %14
%34 = OpUndef %25
%3 = OpVariable %16 Output
%127 = OpConstant %25 2
%1 = OpFunction %18 None %19
%35 = OpLabel
%36 = OpLoad %15 %2
%37 = OpCompositeExtract %14 %36 0
OpBranch %75
%75 = OpLabel
%76 = OpPhi %14 (%37 : %35) (%77 : %78)
%79 = OpPhi %25 (%127 : %35) (%80 : %78)
OpLoopMerge %81 %78 None
OpBranch %82
%82 = OpLabel
%83 = OpBitwiseAnd %25 %79 %31
%84 = OpIEqual %20 %83 %30
OpSelectionMerge %85 None
OpBranchConditional %84 %86 %87
%87 = OpLabel
OpBranch %85
%86 = OpLabel
%88 = OpFMul %14 %76 %76
%89 = OpShiftRightLogical %25 %79 %32
OpBranch %85
%85 = OpLabel
%77 = OpPhi %14 (%88 : %86) (%33 : %87)
%80 = OpPhi %25 (%89 : %86) (%34 : %87)
OpBranch %78
%78 = OpLabel
OpBranchConditional %84 %75 %81
%81 = OpLabel
%90 = OpIEqual %20 %79 %31
OpSelectionMerge %91 None
OpBranchConditional %90 %92 %93
%93 = OpLabel
OpBranch %94
%94 = OpLabel
%95 = OpPhi %14 (%76 : %93) (%96 : %97)
%98 = OpPhi %14 (%76 : %93) (%99 : %97)
%100 = OpPhi %25 (%79 : %93) (%101 : %97)
OpLoopMerge %102 %97 None
OpBranch %103
%103 = OpLabel
%104 = OpUGreaterThan %20 %100 %31
OpSelectionMerge %105 None
OpBranchConditional %104 %106 %107
%107 = OpLabel
OpBranch %105
%106 = OpLabel
%108 = OpShiftRightLogical %25 %100 %32
%109 = OpFMul %14 %95 %95
%110 = OpBitwiseAnd %25 %108 %31
%111 = OpIEqual %20 %110 %31
OpSelectionMerge %112 None
OpBranchConditional %111 %113 %114
%114 = OpLabel
OpBranch %112
%113 = OpLabel
%115 = OpFMul %14 %98 %109
OpBranch %112
%112 = OpLabel
%116 = OpPhi %14 (%115 : %113) (%98 : %114)
OpBranch %105
%105 = OpLabel
%96 = OpPhi %14 (%109 : %112) (%33 : %107)
%99 = OpPhi %14 (%116 : %112) (%33 : %107)
%101 = OpPhi %25 (%108 : %112) (%34 : %107)
OpBranch %97
%97 = OpLabel
OpBranchConditional %104 %94 %102
%102 = OpLabel
OpBranch %91
%92 = OpLabel
OpBranch %91
%91 = OpLabel
%117 = OpPhi %14 (%76 : %92) (%98 : %102)
%119 = OpCompositeConstruct %15 %117 %117 %117 %117
OpStore %3 %119
OpReturn
OpFunctionEnd

Proposal

Support "unrolling" x.powi(2) etcetera into multiplications, i.e. x * x, which are significantly faster.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions