带SSE的8位LERP

如何解决带SSE的8位LERP

我一直在尝试找到使用AMD64 SIMD指令来实现可与大型u8值集配合使用的lerp的最佳方法,但我似乎无法在不需要所有SIMD扩展的情况下找出正确的指令。

我现在正在使用的公式是

u8* a;
u8* b;
u8* result;
size_t count;
u16 total;
u16 progress;

u32 invertedProgress = total - progress;
for(size_t i = 0; i < count; i++){
    result[i] = (u8)((b[i] * progress + a[i] * invertedProgress) / total);
}

我认为它看起来像:

u8* a;
u8* b;
u8* result;
size_t count;
u16 total;
u16 progress;

__m128i mmxZero;
__m128i mmxProgress;
__m128i mmxInvertedProgress;
__m128i mmxProductA;
__m128i mmxProductB;

mmxZero = _mm_xor_ps(zero,zero); // Is there a clear?

mmxProgress = Fill with progress;

mmxTotal = Fill with total;

mmxInvertedProgress = mmxTotal;
mmxInvertedProgress = _mm_unpacklo_epi8(mmxInvertedProgres,mmxZero);
mmxInvertedProgress = _mm_sub_epi8(mmxTotal,progress);

for(size_t i = 0; i < count; i += 8){
    mmxProductA = load A;
    // u8 -> u16
    mmxProductA = _mm_unpacklo_epi8(mmxProductA,mmxZero);
    
    mmxProductB = load B;
    // u8 -> u16
    mmxProductB = _mm_unpacklo_epi8(mmxProductB,mmxZero);

    // a * (total - progress)
    mmxProductA = _mm_mullo_epi16(mmxProductA,mmxInvertedProgress);
    // b * progress
    mmxProductB = _mm_mullo_epi16(mmxProductB,mmxProgress);

    // a * (total - progress) + b * progress
    mmxProductA = _mm_add_epi16(mmxProductA,mmxProductB);
    // (a * (total - progress) + b * progress) / total
    mmxProductA = _mm_div_epi16(mmxProductA,mmxTotal);

    mmxProductA = saturated u16 -> u8; 
    store result = maxProductA;
}

这里有些事情我似乎似乎找不到in the guide,主要与加载和存储值有关。

我知道有一些更新的指令可以同时做更多的事情,这种最初的实现应该可以在较旧的芯片上工作。

在此示例中,我还忽略了对齐方式和缓冲区溢出的可能性,我认为这超出了该问题的范围。

解决方法

好问题。如您所知,SSE没有整数除法指令,并且(与ARM NEON不同)它没有字节的乘法或FMA。

这是我通常要做的。下面的代码将向量分成偶数/奇数字节,使用16位乘法指令分别缩放,然后将它们合并回字节。

// Linear interpolation is based on the following formula: x*(1-s) + y*s which can equivalently be written as x + s(y-x).
class LerpBytes
{
    // Multipliers are fixed point numbers in 16-bit lanes of these vectors,in 1.8 format
    __m128i mulX,mulY;

public:

    LerpBytes( uint16_t progress,uint16_t total )
    {
        // The source and result are bytes.
        // Multipliers only need 1.8 fixed point format,anything above that is wasteful.
        assert( total > 0 );
        assert( progress >= 0 );
        assert( progress <= total );

        const uint32_t fp = (uint32_t)progress * 0x100 / total;
        mulY = _mm_set1_epi16( (short)fp );
        mulX = _mm_set1_epi16( (short)( 0x100 - fp ) );
    }

    __m128i lerp( __m128i x,__m128i y ) const
    {
        const __m128i lowMask = _mm_set1_epi16( 0xFF );

        // Split both vectors into even/odd bytes in 16-bit lanes
        __m128i lowX = _mm_and_si128( x,lowMask );
        __m128i highX = _mm_srli_epi16( x,8 );
        __m128i lowY = _mm_and_si128( y,lowMask );
        __m128i highY = _mm_srli_epi16( y,8 );

        // That multiply instruction has relatively high latency,3-5 cycles.
        // We're lucky to have 4 vectors to handle.
        lowX = _mm_mullo_epi16( lowX,mulX );
        lowY = _mm_mullo_epi16( lowY,mulY );
        highX = _mm_mullo_epi16( highX,mulX );
        highY = _mm_mullo_epi16( highY,mulY );

        // Add the products
        __m128i low = _mm_adds_epu16( lowX,lowY );
        __m128i high = _mm_adds_epu16( highX,highY );

        // Pack them back into bytes.
        // The multiplier was 1.8 fixed point,trimming the lowest byte off both vectors.
        low = _mm_srli_epi16( low,8 );
        high = _mm_andnot_si128( lowMask,high );
        return _mm_or_si128( low,high );
    }
};

static void print( const char* what,__m128i v )
{
    printf( "%s:\t",what );
    alignas( 16 ) std::array<uint8_t,16> arr;
    _mm_store_si128( ( __m128i * )arr.data(),v );
    for( uint8_t b : arr )
        printf( " %02X",(int)b );
    printf( "\n" );
}

int main()
{
    const __m128i x = _mm_setr_epi32( 0x33221100,0x77665544,0xBBAA9988,0xFFEEDDCC );
    const __m128i y = _mm_setr_epi32( 0xCCDDEEFF,0x8899AABB,0x44556677,0x00112233 );
    LerpBytes test( 0,1 );
    print( "zero",test.lerp( x,y ) );
    test = LerpBytes( 1,1 );
    print( "one",2 );
    print( "half",3 );
    print( "1/3",4 );
    print( "1/4",y ) );
    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-