Skip to content

Inconsistent calculations #176

Description

@mmozeiko

These pieces of code calculate different result than scalar paths:

HMM_DotV4:

HandmadeMath/HandmadeMath.h

Lines 987 to 989 in 142ba3c

float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON);
float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult);
float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd);

HMM_LinearCombineV4M4:

HandmadeMath/HandmadeMath.h

Lines 1121 to 1123 in 142ba3c

Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[1].NEON, Left.NEON, 1);
Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[2].NEON, Left.NEON, 2);
Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[3].NEON, Left.NEON, 3);

HMM_MulQ:

HandmadeMath/HandmadeMath.h

Lines 2209 to 2214 in 142ba3c

Result.NEON = vmulq_f32(Right3210, vmulq_f32(vdupq_laneq_f32(Left.NEON, 0), FirstSign));
float32x4_t SecondSign = {1.0f, 1.0f, -1.0f, -1.0f};
Result.NEON = vfmaq_f32(Result.NEON, Right2301, vmulq_f32(vdupq_laneq_f32(Left.NEON, 1), SecondSign));
float32x4_t ThirdSign = {-1.0f, 1.0f, 1.0f, -1.0f};
Result.NEON = vfmaq_f32(Result.NEON, Right1032, vmulq_f32(vdupq_laneq_f32(Left.NEON, 2), ThirdSign));
Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.NEON, Left.NEON, 3);

HMM_DotQ:

HandmadeMath/HandmadeMath.h

Lines 2301 to 2304 in 142ba3c

float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON);
float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult);
float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd);
Result = vgetq_lane_f32(NEONFullAdd, 0);

Example illustrating difference for HMM_DotV4: https://godbolt.org/z/14aEbqa4v

A few unrelated things (I'm too lazy to create new issue):

  • vmulq_f32(vdupq_laneq_f32(a, n), b) can be simplified to vmulq_laneq_f32(b, a, n)

  • this should not use vld4q_f32:

    float32x4x4_t Transposed = vld4q_f32((float*)Matrix.Columns);

    As that forces compiler to store registers to memory for loading back. Instead it should do something like this https://developer.arm.com/documentation/102107a/0100/Floating-point-4x4-matrix-transposition
    Use vld4q_f32 if you guarantee input comes from memory. Sometimes it can be better. But that very much depends on what exactly code is optimized for. Usually you won't want round-trip through memory in such code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions