快速、无分支的 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 绝对差 的相关文章

  • 页面上首次调用 Url.Action 速度很慢

    我有一个相当简单的 ASP MVC 视图的性能问题 这是一个登录页面 应该几乎是即时的 但需要大约半秒钟 经过大量挖掘后 问题似乎出在第一个调用上Url Action 大约需要 450 毫秒 根据迷你分析器 http miniprofile
  • 用于遇到 [...] 的 Haskell Parsec 解析器

    我正在尝试使用 Parsec 在 Haskell 中编写一个解析器 目前我有一个可以解析的程序 test x 1 2 3 end 执行此操作的代码如下 testParser do reserved test v lt identifier
  • Pandas hub_table 更快的替代品

    我正在使用熊猫pivot table在大型数据集 1000 万行 6 列 上运行 由于执行时间至关重要 因此我尝试加快流程 目前 处理整个数据集大约需要 8 秒 这太慢了 我希望找到替代方案来提高速度 性能 我当前的 Pandas 数据透视
  • 红宝石接球和效率

    catch在 Ruby 中意味着跳出深度嵌套的代码 在 Java 中 例如用Java也可以达到同样的效果try catch用于处理异常 但它被认为是糟糕的解决方案 而且效率非常低 在 Ruby 中 我们有处理异常的方法begin raise
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对
  • 除法和乘法 2 的幂

    我在一篇论文中读到 数字除以 2 的幂并乘以 2 的幂是一个微不足道的过程 我在互联网上搜索了很多解释 但没有得到它 任何人都可以用简单的语言解释一下这实际上意味着什么 从位操作的角度来看 这是微不足道的 乘以2相当于左移1位 除法相当于右
  • 过度使用委托对性能来说是一个坏主意吗? [复制]

    这个问题在这里已经有答案了 考虑以下代码 if IsDebuggingEnabled instance Log GetDetailedDebugInfo GetDetailedDebugInfo 可能是一个昂贵的方法 因此我们只想在调试模式
  • 我该如何实现这个折叠功能呢?

    给出了两种数据类型 颜色 和 植物 data Color Red Pink White Blue Purple Green Yellow deriving Show Eq data Plant Leaf Blossom Color Stal
  • 在 monad 转换器类型类中使用列表 monad?

    我的目标是创建一个在 ReaderT WriterT 堆栈或 RWS 堆栈中使用列表 monad 的函数 更一般地说 如何在 mtl 类型类 例如 MonadReader MonadWriter 中使用列表 monad 我为什么要尝试这样做
  • Haskell 标准库是什么?

    GHC专用库可以称为标准库吗 或者只有 Haskell 2010 报告中的那些才算数 许多 GHC 库可以通过 Haskell 报告中的函数来实现 可能与 C 绑定相结合 但其他语言依赖于 GHC 特定的扩展 因为语言报告中定义的当前 Ha
  • 以 O(1) 计算汉明权重 [重复]

    这个问题在这里已经有答案了 在二进制表示中 汉明权重是 1 的数量 我偶然发现了网络并找到了一个 O 1 的答案 v v v gt gt 1 0x55555555 v v 0x33333333 v gt gt 2 0x33333333 in
  • 为 PostgreSQL 查询选择正确的索引

    简化表 CREATE TABLE products product no integer PRIMARY KEY sales integer status varchar 16 category varchar 16 CREATE INDE
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • 独立滚动矩阵的行

    我有一个矩阵 准确地说 是 2d numpy ndarray A np array 4 0 0 1 2 3 0 0 5 我想滚动每一行A根据另一个数组中的滚动值独立地 r np array 2 0 1 也就是说 我想这样做 print np
  • 加快网络抓取速度

    我正在使用一个非常简单的网络抓取工具抓取 23770 个网页scrapy 我对 scrapy 甚至 python 都很陌生 但设法编写了一个可以完成这项工作的蜘蛛 然而 它确实很慢 爬行 23770 个页面大约需要 28 小时 我看过scr
  • 为什么在展开的 ADD 循环内重新初始化寄存器会使其运行速度更快,即使循环内有更多指令?

    我有以下代码 include
  • 我可以让这个 Ruby 代码更快并且/或使用更少的内存吗?

    我有一个Array of StringRuby 中的对象由如下单词组成 animals cat horse dog cat dog bird dog sheep chicken cow 我想将其转换为另一个Array of String对象
  • 如何提高包含大量小图像的 UCollectionView 的性能?

    在我的 iOS 应用程序中我有UICollectionView显示大约 1200 个小 35x35 点 图像 图像存储在应用程序包中 我正确地重用了UICollectionViewCell但仍然存在性能问题 具体取决于我处理图像加载的方式
  • 管道:多个流消费者

    我编写了一个程序来计算语料库中 NGram 的频率 我已经有一个函数 它消耗一串令牌并生成一个订单的 NGram ngram Monad m gt Int gt Conduit t m t trigrams ngram 3 countFre
  • 类型级别集结合律的证明

    我试图证明类型级函数Union https hackage haskell org package type level sets 0 8 5 0 docs Data Type Set html t Union是关联的 但我不确定应该如何完

随机推荐

  • 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