LFSR 实现中的高效位调整

2023-11-25

虽然我有一个很好的 LSFR C 实现,但我想我会在 Haskell 中尝试同样的方法 - 只是看看它是如何进行的。到目前为止,我的想法比 C 实现慢两个数量级,这就引出了一个问题:如何提高性能?显然,位调整操作是瓶颈,分析器也证实了这一点。

这是使用列表和的基线 Haskell 代码Data.Bits:

import           Control.Monad      (when)
import           Data.Bits          (Bits, shift, testBit, xor, (.&.), (.|.))
import           System.Environment (getArgs)
import           System.Exit        (exitFailure, exitSuccess)

tap :: [[Int]]
tap = [
    [],            [],            [],            [3, 2],
    [4, 3],        [5, 3],        [6, 5],        [7, 6],
    [8, 6, 5, 4],  [9, 5],        [10, 7],       [11, 9],
    [12, 6, 4, 1], [13, 4, 3, 1], [14, 5, 3, 1], [15, 14],
    [16,15,13,4],  [17, 14],      [18, 11],      [19, 6, 2, 1],
    [20, 17],      [21, 19],      [22, 21],      [23, 18],
    [24,23,22,17], [25, 22],      [26, 6, 2, 1], [27, 5, 2, 1],
    [28, 25],      [29, 27],      [30, 6, 4, 1], [31, 28],
    [32,22,2,1],   [33,20],       [34,27,2,1],   [35,33],
    [36,25],       [37,5,4,3,2,1],[38,6,5,1],    [39,35],
    [40,38,21,19], [41,38],       [42,41,20,19], [43,42,38,37],
    [44,43,18,17], [45,44,42,41], [46,45,26,25], [47,42],
    [48,47,21,20], [49,40],       [50,49,24,23], [51,50,36,35],
    [52,49],       [53,52,38,37], [54,53,18,17], [55,31],
    [56,55,35,34], [57,50],       [58,39],       [59,58,38,37],
    [60,59],       [61,60,46,45], [62,61,6,5],   [63,62]        ]

xor' :: [Bool] -> Bool
xor' = foldr xor False

mask ::  (Num a, Bits a) => Int -> a
mask len = shift 1 len - 1

advance :: Int -> [Int] -> Int -> Int
advance len tap lfsr
    | d0        = shifted
    | otherwise = shifted .|. 1
    where
        shifted = shift lfsr 1 .&. mask len
        d0 = xor' $ map (testBit lfsr) tap'
        tap' = map (subtract 1) tap

main :: IO ()
main = do
    args <- getArgs
    when (null args) $ fail "Usage: lsfr <number-of-bits>"
    let len = read $ head args
    when (len < 8) $ fail "No need for LFSR"
    let out = last $ take (shift 1 len) $ iterate (advance len (tap!!len)) 0
    if out == 0 then do
        putStr "OK\n"
        exitSuccess
    else do
        putStr "FAIL\n"
        exitFailure

Basically it tests whether the LSFR defined in tap :: [[Int]] for any given bit-length is of maximum-length. (More precisely, it just checks whether the LSFR reaches the initial state (zero) after 2n iterations.)

根据分析器,最昂贵的线路是反馈位d0 = xor' $ map (testBit lfsr) tap'.

到目前为止我尝试过的:

  • use Data.Array:由于没有foldl/r而放弃尝试
  • use Data.Vector:比基线略快

我使用的编译器选项是:-O2, LTS Haskell 8.12 (GHC-8.0.2).

参考 C++ 程序可以在以下位置找到gist.github.com.

不能期望(?)Haskell 代码的运行速度与 C 代码一样快,但是两个数量级太多了,必须有更好的方法来进行位调整。

更新:应用答案中建议的优化的结果

  • 输入为 28 的参考 C++ 程序,使用 LLVM 8.0.0 编译,在我的机器上运行 0.67 秒(与 clang 3.7 相同,稍微慢一些,0.68 秒)
  • 基线 Haskell 代码运行速度大约慢 100 倍(由于空间效率低下,请勿尝试使用大于 25 的输入)
  • 随着重写@托马斯·M·杜布森,仍然使用默认的GHC后端,执行时间下降到5.2s
  • With the rewrite of @Thomas M. DuBuisson, now using the LLVM backend (GHC option -O2 -fllvm), the execution time goes down to 1.7s
    • 使用 GHC 选项-O2 -fllvm -optlc -mcpu=native使其达到 0.73 秒
  • 更换iterate with iterate'当使用 Thomas 的代码时(均使用默认的“本机”后端和 LLVM),@cirdec 的值没有任何区别。然而,它does使用基线代码时会有所不同。

所以,我们的速度从 100 倍到 8 倍再到 1.09 倍,即只比 C 慢 9%!

NoteGHC 8.0.2 的 LLVM 后端需要 LLVM 3.7。在 Mac OS X 上,这意味着安装此版本brew然后进行符号链接opt and llc. See 7.10。 GHC 后端.


预先事项

首先,我在 Intel I5 ~2.5GHz、linux x86-64 上使用 GHC 8.0.1。

第一稿:哦不!速度变慢了!

参数为 25 的起始代码运行:

% ghc -O2 orig.hs && time ./orig 25
[1 of 1] Compiling Main             ( orig.hs, orig.o )
Linking orig ...
OK
./orig 25  7.25s user 0.50s system 99% cpu 7.748 total

所以击败的时间是 77ms——比这个 Haskell 代码好两个数量级。让我们潜入吧。

问题 1:狡猾的代码

我发现代码有几个奇怪的地方。首先是使用shift在高性能代码中。 Shift 支持左移和右移,为此需要一个分支。让我们用更具可读性的 2 的幂来杀死它(shift 1 x ~> 2^x and shift x 1 ~> 2*x):

% ghc -O2 noShift.hs && time ./noShift 25
[1 of 1] Compiling Main             ( noShift.hs, noShift.o )
Linking noShift ...
OK
./noShift 25  0.64s user 0.00s system 99% cpu 0.637 total

(正如您在评论中指出的:是的,这需要调查。可能是先前代码的某些奇怪之处阻止了重写规则的触发,结果是产生了更糟糕的代码)

问题 2:位列表?整数运算拯救了一切!

一项变化,一个数量级。耶。还有什么?好吧,你有一个正在点击的令人尴尬的位位置列表,它看起来像是在乞求低效率和/或依赖于脆弱的优化。此时,我会注意到,对该列表中的任何一项选择进行硬编码都会带来非常好的性能(例如testBit lsfr 24 `xor` testBit lsfr 21)但我们想要一个更通用的快速解决方案。

我建议我们计算掩码all然后,点击位置执行单指令弹出计数。为此,我们只需要一个Int传递给advance而不是整个列表。 popcount 指令需要良好的汇编生成,这需要 llvm 并且可能需要-optlc-mcpu=native或其他非悲观的指令集选择。

这一步给我们pc以下。我已经折叠了警卫移除advance评论中提到了这一点:

let tp = sum $ map ((2^) . subtract 1) (tap !! len)
    pc lfsr = fromEnum (even (popCount (lfsr .&. tp)))
    mask = 2^len - 1
    advance' :: Int -> Int
    advance' lfsr = (2*lfsr .&. mask) .|. pc lfsr 
    out :: Int
    out = last $ take (2^len) $ iterate advance' 0

我们得到的性能是:

% ghc -O2 so.hs -fforce-recomp -fllvm -optlc-mcpu=native && time ./so 25      
[1 of 1] Compiling Main             ( so.hs, so.o )
Linking so ...
OK
./so 25  0.06s user 0.00s system 96% cpu 0.067 total

从开始到结束超过两个数量级,所以希望它与您的 C 语言相匹配。最后,在部署的代码中,具有 C 绑定的 Haskell 包实际上很常见,但这通常是一个教育练习,所以我希望您玩得开心。

编辑:现在可用的 C++ 代码需要我的系统 0.10 (g++ -O3)和 0.12(clang++ -O3 -march=native) 秒,看来我们已经远远超过了我们的目标。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LFSR 实现中的高效位调整 的相关文章

  • 为什么 Parsec 的 sepBy 停止并且不解析所有元素?

    我正在尝试解析一些逗号分隔的字符串 该字符串可能包含也可能不包含具有图像尺寸的字符串 例如 hello world 300x300 good bye world 我写了下面的小程序 import Text Parsec import qua
  • Python 相当于 Bit Twiddling Hacks 中的 C 代码?

    我有一个位计数方法 我正在尝试尽可能快地实现 我想尝试下面的算法位摆弄黑客 http graphics stanford edu seander bithacks html CountBitsSetParallel 但我不知道 C 什么是
  • 持久 selectList 导致错误“无法将类型‘BaseBackend backend0’与‘SqlBackend’匹配”

    我遇到以下编译错误 Couldn t match type BaseBackend backend0 with SqlBackend arising from a use of runSqlite The type variable bac
  • 在依赖类型的函数式编程语言中,扁平化列表是否更容易?

    在 haskell 中寻找一个可以展平任意深度嵌套列表的函数时 即应用的函数concat递归并在最后一次迭代时停止 使用非嵌套列表 我注意到这需要有一个更灵活的类型系统 因为随着列表深度的变化 输入类型也会变化 确实 有几个 stackov
  • 如何看待Python的负数按位运算?

    我发现很难思考 Python 和 Python3 的无限精度负数和按位运算 它不是 32 位或 64 位 这1左边的 s 可以被认为是 无穷多个 它不是很明确 这就是为什么有时很难思考它是如何运作的 似乎一种可行的方法是 总是让它更多 例如
  • 规范化且不可变的数据模型

    Haskell如何解决 规范化不可变数据结构 问题 例如 让我们考虑一个表示前女友 男友的数据结构 data Man Man name String exes Woman data Woman Woman name String exes
  • Haskell 泛化问题(涉及列表理解)

    假设我想知道a上的所有要点 x y 矩形内的平面has 我可以使用列表推导式来计算 如下所示 let myFun2D x y x lt 0 2 y lt 0 2 现在 如果我想为一个人完成同样的事情 x y z 空间 我可以采取同样的方式并
  • Haskell 中的中缀运算符优先级

    对于以下 Haskell 表达式 返回 a gt gt f 应该读作 返回a gt gt f or 返回 a gt gt f 这里的相关规则是什么 规则始终是函数应用程序的优先级高于任何运算符 因此 return a gt gt f 被解析
  • 异或交换可以扩展到两个以上的变量吗?

    我一直在尝试将异或交换扩展到两个以上的变量 例如n变量 但我没有得到比这更好的地方3 n 1 对于两个整型变量x1 and x2你可以像这样交换它们 swap x1 x2 x1 x1 x2 x2 x1 x2 x1 x1 x2 所以 假设你有
  • 标准的能力

    我发现了一些使用标准的旧例子here http www serpentine com blog 2009 09 29 criterion a new benchmarking library for haskell 看起来好像早在 2009
  • 如何在 Haskell 中制作打勾游戏的图案?

    实现有 2 个参数的函数 ticktick 第一个参数是自然数元组 定义游戏场地的行数和列数 第二个列表包含由玩家 x 和玩家 o 轮流玩的坐标给出的井字游戏比赛的记录 打印游戏的实际状态 其中游戏区域将由字符 和 界定 空方块 以及字符
  • 编译器如何实现位域运算?

    当询问如何做的问题时包裹 N 位有符号减法 https stackoverflow com questions 8309538 integer subtraction with wrap around for n bits我得到了以下答案
  • Haskell 中的尾递归字符串分割

    我正在考虑分割字符串的问题s在一个字符处c 这表示为 break c s 其中 Haskell 库定义break c 足够接近 br br s h t if c h then s else let h t br t in h h t 假设我
  • Haskell Stack 从 github 安装包依赖项

    是否可以使用 Haskell 堆栈从 github 安装软件包的版本 例如在一个 cabal or a stack yaml文件 如何在 git repo branch revision 上指向依赖项 对于堆栈 The 的文档stack y
  • 你能识别 Haskell 程序中的无限列表吗? [复制]

    这个问题在这里已经有答案了 可能的重复 如何判断列表是否是无限的 https stackoverflow com questions 7371730 how to tell if a list is infinite 在Haskell中 你
  • : 中缀运算符在 Haskell 中的作用是什么?

    我正在阅读Haskell 简要介绍 http www haskell org tutorial index html 这不是那么温和 并且它反复使用 操作符而不直接解释它的作用 那么 它到底有什么作用呢 是 前置 运算符 x xs 返回一个
  • 检查对以下内容的理解:“变量”与“变量” “价值”、“功能”与“抽象”

    这个问题是后续问题this one https stackoverflow com questions 25327705 is function a sort of variable 25329157 25329157在学习 Haskell
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • Haskell:不在范围内:数据构造函数

    今天开始在学校学习 haskell 我遇到了函数问题 我不明白为什么它不在范围内 代码如下 ff Char gt Char gt Char ff A B x 0 y 1 x lt A y lt B x 1 y 0 和错误 md31 hs 2
  • ST monad 是如何工作的?

    我知道 ST monad 有点像 IO 的弟弟 而 IO 又是添加了状态 monadRealWorld魔法 我可以想象状态 也可以想象 RealWorld 以某种方式放入 IO 中 但每次我写一个类型签名ST the sST monad 的

随机推荐

  • 如何在node.js中实现登录验证

    我有这个节点服务器正在运行 var server http createServer function request responsehttp if request method POST var body request on data
  • 如何测试类型是否是匿名的? [复制]

    这个问题在这里已经有答案了 我有以下方法将对象序列化为 HTML 标记 我只想在类型不是匿名的情况下执行此操作 private void MergeTypeDataToTag object typeData if typeData null
  • 从映射驱动器或共享文件夹运行 .NET 程序

    我编写了一个 C Windows 窗体应用程序 用于将一台计算机上的远程文件夹 源 文件夹是映射驱动器 Z folder 中的文件和文件夹与另一台计算机上的另一个远程文件夹 目标 合并文件夹是共享文件夹的 UNC 路径 computerna
  • 为什么我们总是喜欢在SQL语句中使用参数?

    我对数据库工作非常陌生 现在我可以写了SELECT UPDATE DELETE and INSERT命令 但我见过很多论坛 我们更喜欢这样写 SELECT empSalary from employee where salary salar
  • 如何将文件从远程服务器中的目录 A 移动到目录 B?

    我正在使用 JSch 连接到由 GWT 制作的网站中的 SFTP 我读过一个小例子sftpChannel get sftpChannel rename sftpChannel rm 但我没有找到从远程服务器复制文件的解决方案a目录到远程服务
  • 无法在此事件处理程序中执行操作

    我正在尝试从 DataGridView 中删除一行我使用两种类型的指令 A VouchersDGV Rows Clear B If Not DGV Rows RowIndex IsNewRow Then DGV Rows RemoveAt
  • Matlab 中的高效分类

    我有一张尺寸为 RGB 的图像uint8 576 720 3 我想将每个像素分类为一组颜色 我已经使用rgb2lab从RGB空间到LAB空间 然后删除L层 所以现在是double 576 720 2 由AB组成 现在 我想将其分类为我在另一
  • 总线错误排查

    我正在尝试反转字符串 这是我尝试过的代码 include
  • 编写 C++ 版本的代数游戏 24

    我正在尝试编写一个像游戏 24 一样工作的 C 程序 对于那些不知道如何玩的人 基本上你会尝试通过 四个代数运算符找到 4 个数字总计为 24 的任何方法 和括号 举个例子 假设有人输入 2 3 1 5 2 3 5 1 24 由于括号位置的
  • ADB 安装失败并显示 INSTALL_FAILED_TEST_ONLY

    我在将 apk 安装到我的设备时遇到问题 adb install lt apk gt 使用上述命令将返回以下内容 5413 KB s 99747 bytes in 0 017s pkg data local tmp AppClient Te
  • SQL 的扩展占位符,例如id 在哪里 (??)

    赏金更新 已经从马克那里得到了很好的答案 将 改编为 如下 然而 除了 DBIx 之外 我仍在寻找类似的方案 我只是想兼容anything 我需要有关我为参数化 SQL 语句中的 扩展 占位符选择的语法的建议 因为构建一些构造 IN 子句
  • 带弹性盒的计算器键盘布局

    我正在用 Flexbox 构建一个计算器 我想要其中一个键的高度是两倍 另一个键的宽度是两倍 我用谷歌搜索了很多 但找不到这两个案例 对于两倍高度的键 我发现的唯一答案是flex direction as column 但在这种情况下 我将
  • iPython 笔记本避免在函数内打印

    我想阻止某个函数在 iPython 笔记本中打印 在标准 python 中 可以防止打印问题中回答的某些代码行 防止函数在 Python 的批处理控制台中打印然而 此方法在 iPython Notebook 中不起作用 在内核重新启动之前会
  • 您如何在协作、版本控制的环境中处理 Oracle 软件包? [关闭]

    Closed 这个问题是基于意见的 目前不接受答案 我在 Oracle 的多开发人员环境中工作 有一个大包 我们有DEV gt TST gt PRD的促销模式 目前 所有包编辑都是直接在 TOAD 中进行 然后编译到 DEV 包中 我们遇到
  • 使用 Bash 重定向到多个文件时重定向不明确

    echo gt home jem rep 0 1 3 logs SystemOut log bash home jem rep 0 1 3 logs SystemOut log ambiguous redirect 我可以一次重定向到多个文
  • 如何根据特殊条件进行分组

    目前 当我发出此 SQL 时 它会获取不同的用户名 我有一些不同的用户名 它们代表组 例如GRP BSN 我想将所有其他用户名 恰好是数字 分组到一个组中 例如GRP OTHERS select username count from ho
  • 连接到 SoftHSM java

    Code String pkcs11cfg pkcs11 cfg Provider p new SunPKCS11 pkcs11cfg Security addProvider p KeyStore ks KeyStore getInsta
  • 如何在 Angular cli 6.0.1 中创建 Angular 5 项目

    My 角度 cli 版本是 6 0 1 and node版本是8 11 1 如何创建或添加 Angular 5 的新项目 如果我使用ng 新的 项目名称 然后该项目正在下载角6 我也偶然发现了这个场景 这是我的解决方案 不幸的是 我们受到
  • 找出 Windows 的安装语言为

    我遇到一个问题 用户设置的区域设置 德语 与安装的语言 Windows 英语 不同 有没有办法发现安装的 Windows 语言与用户设置的区域设置 我应该注意的问题是我正在创建共享 并且根据区域设置设置权限 因此如果用户将区域设置设置为德语
  • LFSR 实现中的高效位调整

    虽然我有一个很好的 LSFR C 实现 但我想我会在 Haskell 中尝试同样的方法 只是看看它是如何进行的 到目前为止 我的想法比 C 实现慢两个数量级 这就引出了一个问题 如何提高性能 显然 位调整操作是瓶颈 分析器也证实了这一点 这