这段浮点运算代码性能为何如此低下

2023-04-12 00:59:37 +08:00
 Leon6868
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#define M2T(X) ((unsigned int)(X) / portTICK_PERIOD_MS) // ms to tick

#include "esp_random.h"

#include <sys/time.h>

static const char *TAG = "main";
#define dataLen 14400

void makeMatrixUint8(uint8_t *buf, int len)
{

    for (int i = 0; i < len; i++)
    {
        esp_fill_random(&buf[i], sizeof(uint8_t));
    }
}
void makeMatrixFloat(float *buf, int len)
{

    for (int i = 0; i < len; i++)
    {
        esp_fill_random(&buf[i], sizeof(float));
    }
}

static void testTask()
{
    uint8_t *testData1 = (uint8_t *)malloc(sizeof(uint8_t) * dataLen);
    float *testData2 = (float *)malloc(sizeof(float) * dataLen);
    struct timeval tv_d0;
    struct timeval tv_d1;

    while (1)
    {
        makeMatrixUint8(testData1, dataLen);
        makeMatrixFloat(testData2, dataLen);
        gettimeofday(&tv_d0, NULL);
        for (int t = 0; t < 10; t++)
        {

            for (int i = 0; i < dataLen; i++)
            {
                testData2[i] = testData1[i] * 0.3;
            }
        }
        gettimeofday(&tv_d1, NULL);
        ESP_LOGI(TAG, "%lu", 1000000 * (tv_d1.tv_sec - tv_d0.tv_sec) + (tv_d1.tv_usec - tv_d0.tv_usec));
        
        vTaskDelay(1);
    }
}

void app_main()
{
    xTaskCreate(testTask, "servoTask", 1024 * 4, NULL, tskIDLE_PRIORITY, NULL);
}
1469 次点击
所在节点   嵌入式开发
13 条回复
zk8802
2023-04-12 07:06:37 +08:00
看一下编译生成的二进制代码,里面的浮点运算是软件模拟的浮点指令还是硬件浮点指令?
geekzjj
2023-04-12 08:06:05 +08:00
gettimeofday 以及 log 函数本身的性能损耗测过吗?
biggates
2023-04-12 10:46:39 +08:00
你用 `esp_fill_random()` 的话,是不是只要把整个 `buf` fill 一次就行了,不需要循环去 fill 每个元素?
biggates
2023-04-12 10:53:15 +08:00
原文: This function automatically busy-waits to ensure enough external entropy has been introduced into the hardware RNG state, before returning a new random number. This delay is very short (always less than 100 CPU cycles). 所以你 dataLen 14400 的时候,每次 delay 加起来就很大了。src: https://docs.espressif.com/projects/esp-idf/zh_CN/v4.4.4/esp32/api-reference/system/random.html#_CPPv410esp_randomv
Leon6868
2023-04-12 13:23:34 +08:00
@biggates
@geekzjj
非常抱歉,我的表述有误,问题中的“163813 微秒”指的是代码中通过 `ESP_LOGI(TAG, "%lu", 1000000 * (tv_d1.tv_sec - tv_d0.tv_sec) + (tv_d1.tv_usec - tv_d0.tv_usec));` 输出的时间间隔。刚刚测试了一下,gettimeofday 以及 log 函数损耗都非常小;`esp_fill_random()`没有参与计时,就算移到循环外,整体运算时间都没有变小。
misdake
2023-04-13 09:40:16 +08:00
0.3 改成 0.3f
misdake
2023-04-13 09:48:17 +08:00
godbolt 反汇编: https://godbolt.org/z/or71efds8

double 乘法还是走的软件模拟,类型转换也是。会先后调用 floatsidf muldf3 truncdfsf2 这三个函数,所以很慢吧。
改成 0.3f 之后,就全是正常指令了,float.s mul.s 这类,不需要调用其他方法。
misdake
2023-04-13 10:00:15 +08:00
esp32-s3 模拟: https://wokwi.com/projects/361855375362930689
0.3 打印的时间是 1891678
0.3f 打印的时间是 166518
性能提高 10 倍
misdake
2023-04-13 10:08:48 +08:00
作为对比,去掉* 0.3 的代码,只做类型转换 testData2[i] = testData1[i],模拟器打印时间是 116627 ,和 0.3f 的版本相比性能提升幅度很合理
biggates
2023-04-13 11:51:02 +08:00
@misdake 学习了 感谢
misdake
2023-04-13 12:54:47 +08:00
@biggates 所以解决了吗 我在模拟器上跑的运行时间和你给的运行时间区别还是挺大的
Leon6868
2023-04-14 20:38:42 +08:00
@misdake 非常感谢!
Leon6868
2023-04-14 20:39:55 +08:00
@misdake 加上 f 后,真机上直接快了两个数量级!非常感谢!原来是编译器把这个当成 double 处理了啊 :( 学到了!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/931771

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX