展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 硬件相关 > popcnt指令的前世今生探索

popcnt指令的前世今生探索

最近群聊里传了一个面试题实现统计1的个数(汉明权重hammingWeight),使用popcnt的算法对硬件不友好,有无绕过的思路显然这个哥们的第一个实现是代码语言:javascript代码运行次数:0运行复制inthammingWeight_popcnt(uint64_tn){return__builtin_popcountll(n);}当然c++20也支持https://en.cppreference.com/w/cpp/numeric/popcount一行,觉得自己很帅,当然面试官不

本文目录

    popcnt的前世今生?

    最近群聊里传了一个面试题

    实现统计1的个数(汉明权重 hammingWeight),使用popcnt的算法对硬件不友好,有无绕过的思路

    显然这个哥们的第一个实现是

    代码语言:javascript代码运行次数:0运行复制
    int hammingWeight_popcnt(uint64_t n) {  return __builtin_popcountll(n);}

    当然c++20也支持 https://en.cppreference.com/w/cpp/numeric/popcount

    一行,觉得自己很帅,当然面试官不喜欢,提示不要用popcnt,所谓的对硬件不友好指的应该是部分硬件没有这个指令

    又或者性能原因?难道GPU上的popcnt性能很差?按下不表

    直接贴实现

    代码语言:javascript代码运行次数:0运行复制
    int hammingWeight(uint64_t n) {    int ret = 0;    while (n) {        n &= n - 1;        ret++;    }    return ret;}

    其实开启 O2 加上 -march=native,大家都会生成相同的popcnt, 早在2016年Lemire大哥就发现了

    https://lemire.me/blog/2016/05/23/the-surprising-cleverness-of-modern-compilers/

    附上llvm检测的代码 https://github.com/llvm-mirror/llvm/blob/f36485f7ac2a8d72ad0e0f2134c17fd365272285/lib/Transforms/Scalar/LoopIdiomRecognize.cpp#L960

    只开O2可能保守场景不会生成popcnt

    如果不用popcnt,代码的性能和popcnt差距大吗?或者说,popcnt有危害吗?比如延迟高?

    直接上llvm-mca分析 https://godbolt.org/z/odox8Wdr5

    首先插入一个简单粗暴的教程,如何看懂llvm-mca https://llvm.org/docs/CommandGuide/llvm-mca.html

    就是机器码分析器,模拟机器码执行效果,我们不用装llvm-mca,直接用godbolt内置的工具。代码已经生成好了

    直接贴popcnt代码的结果吧

    代码语言:javascript代码运行次数:0运行复制
    Iterations:        100Instructions:      200Total Cycles:      57Total uOps:        200Dispatch Width:    6uOps Per Cycle:    3.51IPC:               3.51Block RThroughput: 0.5Instruction Info:[1]: #uOps[2]: Latency[3]: RThroughput[4]: MayLoad[5]: MayStore[6]: HasSideEffects (U)[1]    [2]    [3]    [4]    [5]    [6]    Instructions: 1      1     0.25                        popcnt rax, rdi 1      5     0.50                  U     retResources:[0]   - Zn3AGU0[1]   - Zn3AGU1[2]   - Zn3AGU2[3]   - Zn3ALU0[4]   - Zn3ALU1[5]   - Zn3ALU2[6]   - Zn3ALU3[7]   - Zn3BRU1[8]   - Zn3FPP0[9]   - Zn3FPP1[10]  - Zn3FPP2[11]  - Zn3FPP3[12.0] - Zn3FPP45[12.1] - Zn3FPP45[13]  - Zn3FPSt[14.0] - Zn3LSU[14.1] - Zn3LSU[14.2] - Zn3LSU[15.0] - Zn3Load[15.1] - Zn3Load[15.2] - Zn3Load[16.0] - Zn3Store[16.1] - Zn3StoreResource pressure per iteration:[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1] 0.33   0.33   0.34   0.50   0.33   0.33   0.34   0.50    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -     Resource pressure by instruction:[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1] Instructions: -      -      -      -     0.33   0.33   0.34    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     popcnt rax, rdi0.33   0.33   0.34   0.50    -      -      -     0.50    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -     retwarning: found a return instruction in the input assembly sequence.note: program counter updates are ignored.

    先记下这几个数字

    代码语言:javascript代码运行次数:0运行复制
    Iterations:        100Instructions:      200Total Cycles:      57Total uOps:        200Dispatch Width:    6uOps Per Cycle:    3.51IPC:               3.51Block RThroughput: 0.5

    重点参数 是IPC, uOps Per Cycle, 和 Block RThroughput (Block Reciprocal Throughput).

    • IPC就是模拟的总指令数字除以总cycle数 一般这个就表示吞吐了 Instructions / Total Cycles 显然这个值越高越好• Block RThroughput (Block Reciprocal Throughput) 就是Block Throughput(每个cycle能运行几次block)的倒数 就是 Total Cycles / Iterations 每次运行能用几个cycle的意思,显然,这个值越低越好• Instructions / Iterations 代表每次迭代能执行几次指令 显然 Instructions / Iterations / Block RThroughput = IPC 这个数比直接算IPC大点(有误差。。。。)你就当他是无影响的最大IPC吧• (循环数据引用问题可能导致影响。假设循环展开无影响)• uOps Per Cycle,就是模拟的微指令数总和除以总cycle数字 Total uOps/ Total Cycles ,这个和IPC含义差不多,显然这个值越高越好,但是要关注Dispatch Width - uOps Per Cycle 差值, Dispatch Width 表示最大发射指令的并行带宽,相当于资源限制了,uOps Per Cycle表示实际模拟使用的带宽,显然越接近Dispatch Width越说明资源受限制,利用率太高了,相当于CPU高了,需要找到瓶颈来源• 剩下的是执行模拟以及在哪里卡了,具体分析可以用-bottleneck-analysis,得本地搞了godbolt貌似不能玩

    好了,根据上面的godbolt结果,直接把数据差异对比一下

    另外 网上搜到了google的两个实现,把数据补充上 https://godbolt.org/z/9nsczeT5c

    代码语言:javascript代码运行次数:0运行复制
    int hammingWeightV2(uint64_t n) {    n -= (n >> 1) & 0x5555555555555555ULL;    n = ((n >> 2) & 0x3333333333333333ULL) + (n & 0x3333333333333333ULL);    return (((n + (n >> 4)) & 0xF0F0F0F0F0F0F0FULL)            * 0x101010101010101ULL) >> 56;}

    这个实现在一些cpu上有问题 type mismatch。不过一般来说和buildin popcnt一样效果

    代码语言:javascript代码运行次数:0运行复制
    int hammingWeight_popcntV2(uint64_t n) {    int64_t count = 0;    asm("popcnt %1,%0" : "=r"(count) : "rm"(n) : "cc");    return count;}

    实现

    编译器版本

    Dispatch Width

    uOps/Cycle

    IPC

    Block RThroughput

    popcnt

    gcc 13.2

    6

    3.67

    1.83

    1.0

    普通实现

    gcc 13.2

    4

    3.94

    3.54

    2.5

    popcnt

    clang 17.0.1

    6

    4.59

    2.75

    1.0

    普通实现

    clang 17.0.1

    6

    4.78

    3.83

    1.7

    popcnt v2

    gcc 13.2

    6

    2.14

    2.14

    1.3

    手写SWAR

    gcc 13.2

    6

    4.09

    3.72

    3.8

    popcnt v2

    clang 17.0.1

    6

    3.67

    1.83

    1.0

    手写SWAR

    clang 17.0.1

    6

    4.09

    3.72

    3.8

    能看出popcnt的Block RThroughput 低,这显然说明性能更好

    然后看IPC和uOps/Cycle clang的明显比gcc的要高,但汇编说实话一个两行一个一行,这个没啥比较的意义了

    重点和普通实现比,clang生成的汇编要比gcc好一点,Block RThroughput 低 IPC高,且没有特别接近Dispatch Width瓶颈

    但说实话就差一个汇编这点差距根本比不出什么。只能大概说一下popcnt的汇编更少,性能更好而已

    感觉SWAR这种看起来很屌, 但看mca分析感觉不太行 我跑了个qb压测,但是网站挂了,还需要本地跑一下

    https://github.com/wanghenshui/little_bm/blob/dev/hamming_weight/hamming_weight.cc

    我的测试结果来看,SWAR性能反而比popcnt要好,即使Block RThroughput 很高,但IPC也很高,性能反而非常好

    代码语言:javascript代码运行次数:0运行复制
    taskset -c 0  ./hamming_weight2024-02-03T22:39:35+08:00Running ./hamming_weightRun on (16 X 3392.38 MHz CPU s)CPU Caches:  L1 Data 32 KiB (x8)  L1 Instruction 32 KiB (x8)  L2 Unified 512 KiB (x8)  L3 Unified 16384 KiB (x1)Load Average: 0.01, 0.02, 0.00-----------------------------------------------------------------------Benchmark                             Time             CPU   Iterations-----------------------------------------------------------------------BM_hammingWeight_popcnt/0          26.6 ns         26.6 ns     26281110BM_hammingWeight_popcnt/128         265 ns          265 ns      2622989BM_hammingWeight_popcnt/256         526 ns          526 ns      1332695BM_hammingWeight_popcnt/512        1048 ns         1048 ns       666562BM_hammingWeight_popcnt/1024       2096 ns         2095 ns       334434BM_hammingWeight/0                 80.7 ns         80.7 ns      8689750BM_hammingWeight/128               1643 ns         1642 ns       447638BM_hammingWeight/256               3646 ns         3646 ns       195882BM_hammingWeight/512               8099 ns         8097 ns        85508BM_hammingWeight/1024             17193 ns        17190 ns        41208BM_hammingWeightV2/0/0             11.8 ns         11.8 ns     58402778BM_hammingWeightV2/128              118 ns          118 ns      5951445BM_hammingWeightV2/256              233 ns          233 ns      3001257BM_hammingWeightV2/512              463 ns          463 ns      1510925BM_hammingWeightV2/1024             927 ns          927 ns       758631

    不过还需要其他机器测试,我的nuc是r9 6950hx zen3+,性能不错

    github CI机器,SWAR和popcnt就差不多了。

    代码语言:javascript代码运行次数:0运行复制
    Running ./bm_hamming_weightRun on (4 X 2868.73 MHz CPU s)CPU Caches:  L1 Data 32 KiB (x2)  L1 Instruction 32 KiB (x2)  L2 Unified 512 KiB (x2)  L3 Unified 32768 KiB (x1)Load Average: 1.98, 0.54, 0.19-----------------------------------------------------------------------Benchmark                             Time             CPU   Iterations-----------------------------------------------------------------------BM_hammingWeight_popcnt/0          17.5 ns         17.5 ns     39446954BM_hammingWeight_popcnt/128         173 ns          173 ns      4056917BM_hammingWeight_popcnt/256         342 ns          342 ns      2051152BM_hammingWeight_popcnt/512         679 ns          679 ns      1032384BM_hammingWeight_popcnt/1024       1354 ns         1354 ns       517551BM_hammingWeight/0                  124 ns          124 ns      5638895BM_hammingWeight/128               2394 ns         2394 ns       280377BM_hammingWeight/256               5511 ns         5511 ns       123293BM_hammingWeight/512              12036 ns        12036 ns        58336BM_hammingWeight/1024             25711 ns        25710 ns        27149BM_hammingWeightV2/0/0             17.8 ns         17.8 ns     39494382BM_hammingWeightV2/128              182 ns          182 ns      3848768BM_hammingWeightV2/256              360 ns          360 ns      1943778BM_hammingWeightV2/512              709 ns          709 ns       988825BM_hammingWeightV2/1024            1418 ns         1418 ns       493319

    家人们,需要你们的补充测试,各种机器, 复现代码https://github.com/wanghenshui/little_bm 运行build.sh即可

    话说回来,数1到底能干嘛?这里要引入汉明距离 编辑距离相关的概念

    简单理解就是查diff 纠错码之类的效果

    popcnt的来源 http://www.talkchess.com/forum3/viewtopic.php?t=38521

    上个世纪60年代,计算机还属于大型机百花齐放的年代,Control Data Corporation公司的CDC 机器卖的不错,国际象棋也在用这个软件。他们的场景就是棋盘格确认位置,所以实现了popcnt类似的能力,算位置坐标,美国国家安全局(NSA)发现了他们有这个能力,他们的新机器CDC 6000,政府采购并要求加上这个功能,主要是为了类似汉明距离之类的信息统计,相当于变相hash,用来实现校对diff之类的能力,所以也被叫做NSA Instruction (NSA指令)

    这个指令也是那个时代的特殊产物把,算力不行并没有高级的hash能力,只能通过数1模拟,后来CPU性能提升渐渐的都不支持了,然后后来部分CPU支持部分CPU不支持,到现代全都捡回来

    现在的CPU也有很多不支持popcnt指令,以至于游戏客户端领域会有popcnt patch之类的玩意,给玩家打patch绕过popcnt https://github.com/ogurets/popcnt_emulator

    还有什么能用到数1?

    指纹?安全领域,这种更多是汉明距离场景的推广

    能用到bitmap的地方,不过使用bitmap不一定非得算总数

    比如 Hash Array Mapped Tries 结合tries的压缩优点 + bitmap定位槽,

    bitmap浪费所以要压缩一下,位运算躲不了数1场景

    再比如 Succinct Data Structures terarkdb的memtable用的就这玩意,压缩率高

    关于popcnt的信息我就收集到这么多的,大家有其他见解/批评还可以补充一下

    另外,跑一下压测代码!看看你的CPU结果是什么样子的

    参考• https://vaibhavsagar.com/blog/2019/09/08/popcount/ 一些资料汇总在这里搜到的。我一开始是根据群友聊的和关键字搜到hackernews上这篇文章的分享,介绍了背景和部分应用• https://abseil.io/fast/9 这里说的话我很赞同,性能测试是个周期性的工作,可能旧的代码有时候快,后面时代/硬件进步,又慢了 还是要具体机器具体分析• https://github.com/google/supersonic/blob/master/supersonic/utils/bits.h• https://stackoverflow.com/questions/28802692/how-is-popcnt-implemented-in-hardware 这个没看,但感觉现代CPU popcnt代价已经很低了
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    硬件相关 硬件
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。