V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Leon6868
V2EX  ›  嵌入式开发

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

  •  
  •   Leon6868 · 352 天前 · 1411 次点击
    这是一个创建于 352 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 编译环境:ESP-IDF 4.4
    • 运行设备:ESP32S3 ,已经在编译选项中开启性能优化模式,并把 CPU 频率调到了 240MHz 。
    • 目的:模拟 ESP32S3 处理图片,并测试性能
    • 问题:测试时发现,以这个参数执行下来,每次 ESP_LOGI 间隔 163813 微秒,平均下来一次 float 计算要 1 微秒,与芯片性能严重不符。请问是什么原因导致的呢?
    #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);
    }
    
    13 条回复    2023-04-14 20:39:55 +08:00
    zk8802
        1
    zk8802  
       351 天前
    看一下编译生成的二进制代码,里面的浮点运算是软件模拟的浮点指令还是硬件浮点指令?
    geekzjj
        2
    geekzjj  
       351 天前 via Android
    gettimeofday 以及 log 函数本身的性能损耗测过吗?
    biggates
        3
    biggates  
       351 天前
    你用 `esp_fill_random()` 的话,是不是只要把整个 `buf` fill 一次就行了,不需要循环去 fill 每个元素?
    biggates
        4
    biggates  
       351 天前
    原文: 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
        5
    Leon6868  
    OP
       351 天前
    @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
        6
    misdake  
       350 天前 via Android
    0.3 改成 0.3f
    misdake
        7
    misdake  
       350 天前 via Android   ❤️ 2
    godbolt 反汇编: https://godbolt.org/z/or71efds8

    double 乘法还是走的软件模拟,类型转换也是。会先后调用 floatsidf muldf3 truncdfsf2 这三个函数,所以很慢吧。
    改成 0.3f 之后,就全是正常指令了,float.s mul.s 这类,不需要调用其他方法。
    misdake
        8
    misdake  
       350 天前 via Android   ❤️ 1
    esp32-s3 模拟: https://wokwi.com/projects/361855375362930689
    0.3 打印的时间是 1891678
    0.3f 打印的时间是 166518
    性能提高 10 倍
    misdake
        9
    misdake  
       350 天前 via Android   ❤️ 1
    作为对比,去掉* 0.3 的代码,只做类型转换 testData2[i] = testData1[i],模拟器打印时间是 116627 ,和 0.3f 的版本相比性能提升幅度很合理
    biggates
        10
    biggates  
       350 天前
    @misdake 学习了 感谢
    misdake
        11
    misdake  
       350 天前 via Android
    @biggates 所以解决了吗 我在模拟器上跑的运行时间和你给的运行时间区别还是挺大的
    Leon6868
        12
    Leon6868  
    OP
       349 天前
    @misdake 非常感谢!
    Leon6868
        13
    Leon6868  
    OP
       349 天前
    @misdake 加上 f 后,真机上直接快了两个数量级!非常感谢!原来是编译器把这个当成 double 处理了啊 :( 学到了!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1045 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:17 · PVG 03:17 · LAX 12:17 · JFK 15:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.