快速、无分支的 unsigned int 绝对差

2024-03-05

我有一个程序,它花费大部分时间计算 RGB 值之间的欧几里德距离(无符号 8 位的 3 元组)Word8)。我需要一个快速、无分支的 unsigned int 绝对差函数,这样

unsigned_difference :: Word8 -> Word8 -> Word8
unsigned_difference a b = max a b - min a b

尤其,

unsigned_difference a b == unsigned_difference b a

我使用 GHC 7.8 中的新 primops 得出了以下结论:

-- (a < b) * (b - a) + (a > b) * (a - b)
unsigned_difference (I# a) (I# b) =
    I# ((a <# b) *# (b -# a) +# (a ># b) *# (a -# b))]

which ghc -O2 -S编译为

.Lc42U:
    movq 7(%rbx),%rax
    movq $ghczmprim_GHCziTypes_Izh_con_info,-8(%r12)
    movq 8(%rbp),%rbx
    movq %rbx,%rcx
    subq %rax,%rcx
    cmpq %rax,%rbx
    setg %dl
    movzbl %dl,%edx
    imulq %rcx,%rdx
    movq %rax,%rcx
    subq %rbx,%rcx
    cmpq %rax,%rbx
    setl %al
    movzbl %al,%eax
    imulq %rcx,%rax
    addq %rdx,%rax
    movq %rax,(%r12)
    leaq -7(%r12),%rbx
    addq $16,%rbp
    jmp *(%rbp)

编译用ghc -O2 -fllvm -optlo -O3 -S生成以下 asm:

.LBB6_1:
    movq    7(%rbx), %rsi
    movq    $ghczmprim_GHCziTypes_Izh_con_info, 8(%rax)
    movq    8(%rbp), %rcx
    movq    %rsi, %rdx
    subq    %rcx, %rdx
    xorl    %edi, %edi
    subq    %rsi, %rcx
    cmovleq %rdi, %rcx
    cmovgeq %rdi, %rdx
    addq    %rcx, %rdx
    movq    %rdx, 16(%rax)
    movq    16(%rbp), %rax
    addq    $16, %rbp
    leaq    -7(%r12), %rbx
    jmpq    *%rax  # TAILCALL

因此,LLVM 设法用(更有效?)条件移动指令代替比较。不幸的是编译时使用-fllvm对我的程序的运行时间影响不大。

然而,这个功能有两个问题。

  • 我想比较Word8,但是比较 primops 需要使用Int。这会导致不必要的分配,因为我被迫存储 64 位Int而不是一个Word8.

我已经分析并确认了使用fromIntegral :: Word8 -> Int占该计划总拨款的 42.4%。

  • 我的版本使用 2 次比较、2 次乘法和 2 次减法。我想知道是否有更有效的方法,使用按位运算或 SIMD 指令并利用我正在比较的事实Word8.

我之前已经标记过这个问题C/C++以吸引那些更倾向于位操作的人的注意。我的问题使用 Haskell,但我会接受以任何语言实现正确方法的答案。

结论:

我决定使用

w8_sad :: Word8 -> Word8 -> Int16
w8_sad a b = xor (diff + mask) mask
    where diff = fromIntegral a - fromIntegral b
          mask = unsafeShiftR diff 15

因为它比我原来的要快unsigned_difference功能齐全,实现简单。 Haskell 中的 SIMD 内在函数尚未成熟。因此,虽然 SIMD 版本速度更快,但我决定使用标量版本。


好吧,我试着进行了一些基准测试。我用标准 http://hackage.haskell.org/package/criterion-0.8.0.1对于基准,因为它做了适当的显着性测试。我也用快速检查 http://hackage.haskell.org/package/QuickCheck这里确保所有方法返回相同的结果。

我使用 GHC 7.6.3 进行编译(不幸的是,我无法包含您的 primops 函数)并使用-O3:

ghc -O3 AbsDiff.hs -o AbsDiff && ./AbsDiff

首先,我们可以看到简单的实现和一些小改动之间的区别:

absdiff1_w8 :: Word8 -> Word8 -> Word8
absdiff1_w8 a b = max a b - min a b

absdiff2_w8 :: Word8 -> Word8 -> Word8
absdiff2_w8 a b = unsafeCoerce $ xor (v + mask) mask
  where v = (unsafeCoerce a::Int64) - (unsafeCoerce b::Int64)
        mask = unsafeShiftR v 63

Output:

benchmarking absdiff_Word8/1
mean: 249.8591 us, lb 248.1229 us, ub 252.4321 us, ci 0.950
....

benchmarking absdiff_Word8/2
mean: 202.5095 us, lb 200.8041 us, ub 206.7602 us, ci 0.950
...

我用绝对整数值 http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs来自“Bit Twiddling Hacks here”的技巧。不幸的是我们需要强制转换,我认为不可能在以下领域很好地解决问题Word8单独使用,但无论如何使用本机整数类型似乎都是明智的(但绝对不需要创建堆对象)。

它看起来并不是很大的差异,但我的测试设置也不完美:我将函数映射到大量随机值上,以排除分支预测,从而使分支版本看起来比实际更有效。这会导致 thunk 在内存中积累,这可能会极大地影响时间。当我们减去维护列表的恒定开销时,我们很可能会看到比 20% 的加速要多得多的结果。

生成的程序集实际上非常好(这是该函数的内联版本):

.Lc4BB:
    leaq 7(%rbx),%rax
    movq 8(%rbp),%rbx
    subq (%rax),%rbx
    movq %rbx,%rax
    sarq $63,%rax
    movq $base_GHCziInt_I64zh_con_info,-8(%r12)
    addq %rax,%rbx
    xorq %rax,%rbx
    movq %rbx,0(%r12)
    leaq -7(%r12),%rbx
    movq $s4z0_info,8(%rbp)

正如预期的那样,1 次减法、1 次加法、1 次右移、1 次异或且无分支。使用 LLVM 后端并不会显着改善运行时间。

如果您想尝试更多东西,希望这对您有用。

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where

import Data.Word
import Data.Int
import Data.Bits
import Control.Arrow ((***))
import Control.DeepSeq (force)
import Control.Exception (evaluate)
import Control.Monad
import System.Random
import Unsafe.Coerce

import Test.QuickCheck hiding ((.&.))
import Criterion.Main

absdiff1_w8 :: Word8 -> Word8 -> Word8
absdiff1_w8 !a !b = max a b - min a b

absdiff1_int16 :: Int16 -> Int16 -> Int16
absdiff1_int16 a b = max a b - min a b

absdiff1_int :: Int -> Int -> Int
absdiff1_int a b = max a b - min a b

absdiff2_int16 :: Int16 -> Int16 -> Int16
absdiff2_int16 a b = xor (v + mask) mask
  where v = a - b
        mask = unsafeShiftR v 15

absdiff2_w8 :: Word8 -> Word8 -> Word8
absdiff2_w8 !a !b = unsafeCoerce $ xor (v + mask) mask
  where !v = (unsafeCoerce a::Int64) - (unsafeCoerce b::Int64)
        !mask = unsafeShiftR v 63

absdiff3_w8 :: Word8 -> Word8 -> Word8
absdiff3_w8 a b = if a > b then a - b else b - a

{-absdiff4_int :: Int -> Int -> Int-}
{-absdiff4_int (I# a) (I# b) =-}
    {-I# ((a <# b) *# (b -# a) +# (a ># b) *# (a -# b))-}

e2e :: (Enum a, Enum b) => a -> b
e2e = toEnum . fromEnum

prop_same1 x y = absdiff1_w8 x y == absdiff2_w8 x y
prop_same2 (x::Word8) (y::Word8) = absdiff1_int16 x' y' == absdiff2_int16 x' y'
    where x' = e2e x
          y' = e2e y

check = quickCheck prop_same1
     >> quickCheck prop_same2

instance (Random x, Random y) => Random (x, y) where
  random gen1 =
    let (x, gen2) = random gen1
        (y, gen3) = random gen2
    in ((x,y),gen3)

main =
    do check
       !pairs_w8 <- fmap force $ replicateM 10000 (randomIO :: IO (Word8,Word8))
       let !pairs_int16 = force $ map (e2e *** e2e) pairs_w8
       defaultMain
         [ bgroup "absdiff_Word8" [ bench "1" $ nf (map (uncurry absdiff1_w8)) pairs_w8
                                  , bench "2" $ nf (map (uncurry absdiff2_w8)) pairs_w8
                                  , bench "3" $ nf (map (uncurry absdiff3_w8)) pairs_w8
                                  ]
         , bgroup "absdiff_Int16" [ bench "1" $ nf (map (uncurry absdiff1_int16)) pairs_int16
                                  , bench "2" $ nf (map (uncurry absdiff2_int16)) pairs_int16
                                  ]
         {-, bgroup "absdiff_Int"   [ bench "1" $ whnf (absdiff1_int 13) 14-}
                                  {-, bench "2" $ whnf (absdiff3_int 13) 14-}
                                  {-]-}
         ]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

快速、无分支的 unsigned int 绝对差 的相关文章

  • 系数函数速度慢

    请考虑 Clear x expr Sum x i i 15 30 CoefficientList expr x Timing Coefficient Expand expr x 234 Timing Coefficient expr x 2
  • 预填充 UICollectionView 单元重用队列

    问题 我有一个应用程序 只有一个UICollectionView我第一次滚动它时很卡顿 我已将来源范围缩小到正在创建新单元格 2 的事实 使用initWithFrame 因为周围没有可以重复使用的细胞 初始滚动后 重用队列不为空 单元格可以
  • 从 Golang 调用 C 函数

    我想在 Golang 中编写控制器逻辑并处理 json 和数据库 同时在 C 中使用我的数学处理模型 在我看来 调用 C 函数的开销必须尽可能低 就像设置寄存器 rcx rdx rsi rdi 一样 执行一些操作fastcall 并获取 r
  • 公共领域还好吗?

    在你像我最初那样做出直觉反应之前 请阅读整个问题 我知道它们让你感觉很脏 我知道我们以前都被烧伤过 我知道这不是 好风格 但是公共场所可以吗 我正在开发一个相当大规模的工程应用程序 该应用程序创建并使用结构的内存模型 从高层建筑到桥梁再到棚
  • Haskell Cabal:“包间接依赖于同一包的多个版本”

    清除我的所有后cabal installed 包 我运行了以下会话 cabal update Downloading the latest package list from hackage haskell org james bast c
  • 如何与更高级别的类型合作

    玩弄教堂的数字 我遇到了无法指导 GHC 类型检查器处理高阶类型的情况 首先我写了一个版本 没有任何类型签名 module ChurchStripped where zero z z inc n z s s n z s natInteger
  • 如何加快 Java VM (JVM) 的启动时间?

    我正在运行启动多个 JVM 进程的测试 与 JVM 内运行的实际测试时间相比 JVM 的总结启动时间非常重要 我怎样才能加快速度 我已经使用了 client 选项 这确实有帮助 但没有我想要的那么多 还有其他方法吗 比如预加载一堆 JVM
  • 为什么 GHC 在这里推断出单态类型,即使禁用了单态限制?

    这是由解析 f f pure 的类型 https stackoverflow com questions 55388119 resolving the type of f f pure 55388309 noredirect 1 comme
  • Haskell 中的“修复”是什么?为什么“修复错误”会打印无限字符串?为什么“拿 10 美元修复错误”也有同样的作用?

    长话短说 我在看西蒙 佩顿 琼斯的演讲 https www youtube com watch v re96UgMk6GQ 并且当时21 41 https youtu be re96UgMk6GQ t 1301他引用了一句话 我正在解决一个
  • 为什么 b = (b - x) & x 会得到下一个子集?

    The 有竞争力的程序员手册 https cses fi book book pdf第 99 页建议使用以下方法来遍历集合的所有子集x 集合位代表集合中的数字 int b 0 do Process subset b while b b x
  • 为什么在 data.frame 中预先指定类型会比较慢?

    我预先分配了一个大 data frame 以便稍后填写 我通常这样做NA是这样的 n lt 1e6 a lt data frame c1 1 n c2 NA c3 NA 我想知道如果我预先指定数据类型是否会让事情变得更快 所以我测试了 f1
  • Haskell/Idris 中的开放类型级别证明

    在 Idris Haskell 中 可以通过注释类型并使用 GADT 构造函数 例如使用 Vect 来证明数据的属性 但这需要将属性硬编码到类型中 例如 Vect 必须是与 List 不同的类型 是否有可能拥有具有开放属性集的类型 例如同时
  • 浏览器如何异步执行Javascript并渲染

    这是jsfiddle上的代码
  • C++ OpenCV imdecode 慢

    我将图像的字节数组从 C 发送到 C 库 我使用 OpenCV 版本 3 3 1 解码图像 BMP 图像解码速度很快 但 JPEG 图像解码速度很慢 如何加快 JPEG 图像的解码时间 多线程 GPU 解码性能 Resolution For
  • 由于内容不可压缩,谷歌浏览器中出现了新的复合层

    当 chrome profiler 说 图层是单独合成的 因为它无法被挤压 时 它到底意味着什么 我正在对我的 html 进行更改 并在相对 div 内引入了一个固定位置 div 并给出了will change transform在上面 完
  • 承诺的反面是什么?

    承诺代表将来可能可用 或无法实现 的值 我正在寻找的是一种数据类型 它表示将来可能变得不可用的可用值 可能是由于错误 Promise a b TransitionFromTo
  • 如何调试性能问题/优化您的流星应用程序

    我刚刚将 Meteor 应用程序部署到 Digital Ocean 上的生产服务器上 我注意到 对于大约 7500 个文档 完全获取对象 有选择地仅获取 3 个字段 并填充自动完成数据大约需要 3 5 秒 我相信对于如此数量的数据来说 它应
  • Java中精确的时间测量

    Java 提供了两种获取当前时间的方法 System nanoTime and System currentTimeMillis 第一个给出的结果以纳秒为单位 但实际精度比这要差得多 许多微秒 JVM 是否已经为每台特定机器提供了最佳的价值
  • 非规范化如何提高数据库性能?

    我听说过很多关于非规范化的内容 它是为了提高某些应用程序的性能而进行的 但我从来没有尝试过做任何相关的事情 所以 我只是好奇 规范化数据库中的哪些地方会使性能变差 或者换句话说 非规范化原则是什么 如果我需要提高性能 如何使用此技术 非规范
  • 如何在 PHP 数组中的另一个已知(通过键或指针)元素之后有效地插入元素?

    给定一个数组 a array abc 123 k1 gt v1 k2 gt v2 78 tt k3 gt v3 当其内部指针指向其元素之一时 如何在当前元素之后插入元素 如何在键已知元素 例如 k1 之后插入元素 表现护理 您可以通过使用拆

随机推荐

  • PyTorch 无法检测 CUDA

    我在 PyTorch 上运行 CNN torch cuda is available 函数返回 false 并且未检测到 GPU 不过 我可以使用 GPU 运行 Keras 模型 这是我的系统信息 操作系统 Ubuntu 18 04 3 P
  • 为什么双向 ManyToOne 会导致 Hibernate 中的循环依赖?

    我的项目中有实体 基于Spring Boot Hibernate Entity Table name user account public class UserAccount Id GeneratedValue strategy Gene
  • Angularjs 在控制器之间共享方法

    我有一个应用程序 它在一个页面 主页 上显示新闻提要 在另一个页面上仅显示用户的提要 用户个人资料页面 两个页面的外观和行为方式相同 内容的变化是由于调用了不同的URL 在AngularJS中如何解决这个问题 我有一个家庭控制器 它具有用于
  • 为什么使用 redux 来实现不可变状态

    我正在学习 redux 并且正在努力理解为什么状态必须是不可变的 您能否为我提供一个示例 最好是代码 其中打破不可变合约会导致不那么明显的副作用 Redux 最初是为了演示 时间旅行调试 的理念而发明的 能够在分派操作的历史记录中来回查看
  • Eclipse:如何刷新整个工作区? F5 不行

    我有一个包含一堆 java 项目的工作区 如果我去File gt Refresh 它并没有真正刷新任何内容 可能是当前选择的项目 如何让 eclipse 刷新all的项目 It will indeed only refresh the cu
  • Java8的Collection.parallelStream如何工作?

    Collection类带有一个新方法 parallelStream 在 Java SDK 8 中 显然 这种新方法提供了一种并行消费集合的机制 但是 我想知道Java是如何实现这种并行性的 其根本机制是什么 它只是多线程执行吗 或者 for
  • 为什么 WCF 有时会在生成的代理类型末尾添加“Field”?

    基本上 我有一个带有成员 X 和 Y 的服务器端类型 Foo 每当我使用 Visual Studio 的 添加服务器引用 时 我都会看到 WSDL 和生成的代理都将单词 Field 附加到所有成员并更改第一个字母的大小写 IE 中 X 和
  • 多处理 - 使用管理器命名空间来节省内存

    我有几个进程 每个进程都完成需要单个大 numpy 数组的任务 这只是被读取 线程正在搜索适当的值 如果每个进程都加载数据 我会收到内存错误 因此 我试图通过使用管理器在进程之间共享相同的数组来最小化内存使用量 但是我仍然收到内存错误 我可
  • 在 Python 中替换 XML 元素

    我试图用一组新的坐标替换 bbox 内部的元素 我的代码 import element tree import xml etree ElementTree as ET import xml file tree ET parse C high
  • 如何使用 argparse 为参数创建可选值?

    我正在创建一个 python 脚本 我想要一个参数来控制作为输出获得的搜索结果数量 我目前已命名该参数 head 这是我希望它具有的功能 When head未在命令行中传递我希望它默认为一个值 在这种情况下 一个相当大的 比如 80 Whe
  • 通过 FFmpeg 将过滤器添加到 Instagram 或 Snapchat 等视频

    我在用FFmpeg在我的 Android 应用程序中 我已经在视频上成功实现了以下滤镜 效果 反转颜色 黑与白 Sepia Vignette 伽玛效应 我关注了 FFmpeg 视频过滤器文档 还有类似的问题 https stackoverf
  • Azure AD B2C 在用户中导入

    我需要创建一个 B2C 目录并使用该图从旧的基于 NET 会员资格的应用程序导入成员 所以我遵循了这个教程https learn microsoft com en us azure active directory b2c active d
  • 高速高效更新 QTableView

    我使用带有 QItemDelegate 子类的 QTableView 来控制表视图单元格的外观和感觉 每个单元格显示外部连接设备的名称和状态 一次最多可以连接 100 个设备 每个设备的名称和类型本质上是静态的 很少更新 可能每小时一次 但
  • mongodb num_rows 相当于 php

    我怎样才能得到结果的数量 相当于num rows mysqli 在mongodb 如果我有 db gt dbName gt find array email gt newemail password gt newpass 检查符合此条件的结
  • 深入了解 skew() 函数

    我真的需要了解如何skew xdeg 函数有效所有研究似乎都没有解释 x 角度如何影响其他点并像这样扭曲它 我需要知道是否有任何数学公式或一种方法可以预期使用特定角度的结果 附 我已经阅读了大量文档 其中最好的一个是DevDocs其中说 这
  • 当 R 中的生存分析中违反比例假设时,如何对协变量与时间的相互作用进行建模

    在 R 中 当比例检验 使用 coxph 显示违反了 Cox 模型中的比例假设时 合并协变量和时间之间的交互项的最佳方法是什么 我知道您可以使用分层或与时间项交互 我对后者感兴趣 我无法在互联网上找到明确的解释以及如何执行此操作的示例 在使
  • 如何使用数字序列解压可变参数模板参数?

    如何 或者是否可以 使用数字序列解压参数包 例如 template
  • Android - 自定义小部件未更新

    我正在尝试为我的应用程序制作一个小部件 但它没有更新 我只需要更改文本视图文本并在按下按钮时打开一个活动 但它们都不起作用 代码 public void onUpdate Context context AppWidgetManager a
  • Xcode 10 和 super.tearDown

    从 Xcode 10 1 可能是 10 开始 当我创建单元测试文件时 我没有调用 super tearDown 和 super setUp 我在发行说明中没有看到这样的变化 在文档中https developer apple com doc
  • 快速、无分支的 unsigned int 绝对差

    我有一个程序 它花费大部分时间计算 RGB 值之间的欧几里德距离 无符号 8 位的 3 元组 Word8 我需要一个快速 无分支的 unsigned int 绝对差函数 这样 unsigned difference Word8 gt Wor