why mixed FMUL and FMLA is slower than pure FMUL or FMLA on Cortex-A53

Viewed 374

There are 3 code blocks:

A:

asm volatile(
    "fmla   v0.4s, v30.4s, v22.s[0]  \r\n"
    "fmla   v1.4s, v31.4s, v22.s[0]  \r\n"
    "fmla   v2.4s, v30.4s, v22.s[1]  \r\n"
    "fmla   v3.4s, v31.4s, v22.s[1]  \r\n"
    "fmla   v4.4s, v30.4s, v22.s[2]  \r\n"
    "fmla   v5.4s, v31.4s, v22.s[2]  \r\n"
    "fmla   v6.4s, v30.4s, v22.s[3]  \r\n"
    "fmla   v7.4s, v31.4s, v22.s[3]  \r\n"
    "fmla   v8.4s, v30.4s, v23.s[0]  \r\n"
    "fmla   v9.4s, v31.4s, v23.s[0]  \r\n"
    "fmla  v10.4s, v30.4s, v23.s[1]  \r\n"
    "fmla  v11.4s, v31.4s, v23.s[1]  \r\n"
    "fmla  v12.4s, v30.4s, v23.s[2]  \r\n"
    "fmla  v13.4s, v31.4s, v23.s[2]  \r\n"
    "fmla  v14.4s, v30.4s, v23.s[3]  \r\n"
    "fmla  v15.4s, v31.4s, v23.s[3]  \r\n"
    :
    :
    : "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
      "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");

B:

asm volatile(
    "fmul   v0.4s, v30.4s, v22.s[0]  \r\n"
    "fmul   v1.4s, v31.4s, v22.s[0]  \r\n"
    "fmul   v2.4s, v30.4s, v22.s[1]  \r\n"
    "fmul   v3.4s, v31.4s, v22.s[1]  \r\n"
    "fmul   v4.4s, v30.4s, v22.s[2]  \r\n"
    "fmul   v5.4s, v31.4s, v22.s[2]  \r\n"
    "fmul   v6.4s, v30.4s, v22.s[3]  \r\n"
    "fmul   v7.4s, v31.4s, v22.s[3]  \r\n"
    "fmul   v8.4s, v30.4s, v23.s[0]  \r\n"
    "fmul   v9.4s, v31.4s, v23.s[0]  \r\n"
    "fmul  v10.4s, v30.4s, v23.s[1]  \r\n"
    "fmul  v11.4s, v31.4s, v23.s[1]  \r\n"
    "fmul  v12.4s, v30.4s, v23.s[2]  \r\n"
    "fmul  v13.4s, v31.4s, v23.s[2]  \r\n"
    "fmul  v14.4s, v30.4s, v23.s[3]  \r\n"
    "fmul  v15.4s, v31.4s, v23.s[3]  \r\n"
    :
    :
    : "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
      "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");

C:

asm volatile(
    "fmla   v0.4s, v30.4s, v22.s[0]  \r\n"
    "fmla   v1.4s, v31.4s, v22.s[0]  \r\n"
    "fmla   v2.4s, v30.4s, v22.s[1]  \r\n"
    "fmla   v3.4s, v31.4s, v22.s[1]  \r\n"
    "fmla   v4.4s, v30.4s, v22.s[2]  \r\n"
    "fmla   v5.4s, v31.4s, v22.s[2]  \r\n"
    "fmla   v6.4s, v30.4s, v22.s[3]  \r\n"
    "fmla   v7.4s, v31.4s, v22.s[3]  \r\n"
    "fmla   v8.4s, v30.4s, v23.s[0]  \r\n"
    "fmla   v9.4s, v31.4s, v23.s[0]  \r\n"
    "fmla  v10.4s, v30.4s, v23.s[1]  \r\n"
    "fmla  v11.4s, v31.4s, v23.s[1]  \r\n"
    "fmla  v12.4s, v30.4s, v23.s[2]  \r\n"
    "fmla  v13.4s, v31.4s, v23.s[2]  \r\n"
    "fmla  v14.4s, v30.4s, v23.s[3]  \r\n"
    "fmul  v15.4s, v31.4s, v23.s[3]  \r\n"
    :
    :
    : "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
      "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");

The only difference between A and B is FMLA vs. FMUL, and the only difference

between A and C is the last FMLA instruction is changed to FMUL

And there will be some codes to execute each block many times and record the time cost:

auto start=std::chrono::high_resolution_clock::now();
for(int i=0; i<1000*1000*1000; i++) {
    <Block A or B or C>;
}
auto end=std::chrono::high_resolution_clock::now();
printf("%f\n", std::chrono::duration<double, std::milli>(end-start).count());

But the result is A and B use almost the same time, while C is much more slower than A and B (cost near 30% more time than A and B).

Test environment:

Raspbery Pi 3 with pi64 (https://github.com/bamarni/pi64 a 64bit Debian OS)

compiler: g++ 7.2.0

By the way, I have tested this on some other ARMv8 CPU:

 SoC or Board            CPU Micro-arch           Result
Raspbery Pi 3              Cortex-A53              Slow
  A MTK Soc                Cortex-A53              Slow
Snapdragon 820          Kyro by Qualcomm           Same
  NVIDIA TX2               Cortex-A57              Same
  NVIDIA TX2            Denver2 by NVIDIA          Same

It seem's this problem will only appear on Cortex-A53 or on in-order micro-architecture.

I have read the deassembled code and it looks fine.

0 Answers
Related