搞定“超超超难”剑桥面试数学题番外篇:ARM64汇编

news2024/11/23 3:41:42

在这里插入图片描述

0. 概览

在 有趣的小实验:四种语言搞定“超超超难”剑桥面试数学题 那篇博文中,我们使用 4 种语言(x64汇编、C、Swift 以及 Ruby)实现了一道算法题。

不过,其中的汇编语言对应的是 intel CPU 上的 x64 指令集,那么能不能在 Apple Silicon 芯片(M1,M2)上使用汇编来原生实现相同的功能呢?

答案是肯定的!

在本篇博文中,我们将会在 M2 芯片的 Mac 上使用 ARM64 汇编语言完成算法题的挑战。

本文测试环境为:M2 芯片 + 16GB 内存的 MBA,macOS Ventura 13.4

闲言少叙,让我们躁起来 😉

Let’s go!!!


1. 情景重现

让我们回顾一下原题,这是一道剑桥面试题(如下图所示),图中用了 4 个 ‘Super’ 来形容它的“超超超超级难”:

在这里插入图片描述

题目的意思很简单:

  • 如果 a + b + c + d = 63;
  • 求 ab + bc + cd 的最大值;
  • 其中 a、b、c、d 都为自然数;

如果说用用数学证明它“难得离谱”的话,那么编程来解决则恰恰相反:没有比写代码搞定它更容易的事了。

2. M2 芯片上的重测结果

首先,我们用之前博文中相同的 Ruby、Swift 和 C 代码生成可执行文件,在 M2 上重新运行一下,看看速度如何。

为了方便小伙伴们阅览,下面把每种语言原来的代码一并奉上。

2.1 Ruby 语言

#!/usr/bin/ruby

max = 0
r = 1..63
for a in r do
    for b in r do
        for c in r do
            for d in r do
                if a + b + c + d == 63
                    rlt = a*b + b*c + c*d
                    if rlt >= max 
                        max = rlt
                    end
                end
            end
        end
    end
end

print("max is #{max}\n")

连续执行 3 次,平均耗时大约 0.7 秒左右:

% time ./test.rb
max is 991
./test.rb  0.67s user 0.03s system 99% cpu 0.701 total
% time ./test.rb
max is 991
./test.rb  0.67s user 0.03s system 99% cpu 0.706 total
% time ./test.rb
max is 991
./test.rb  0.67s user 0.03s system 99% cpu 0.702 total

2.2 Swift 语言

import Foundation

typealias GroupNumbers = (a: Int, b: Int, c: Int, d: Int, rlt: Int)

@inline(__always) func value(_ g: GroupNumbers) -> Int {
    g.a * g.b + g.b * g.c + g.c * g.d
}

var max = 0
let r = 1...63
for a in r {
    for b in r {
        for c in r {
            for d in r {
                if a + b + c + d == 63 {
                    let v = (a: a, b: b, c: c, d: d, rlt: 0)
                    let rlt = value(v)
                    
                    if rlt >= max {
                        max = rlt
                    }
                }
            }
        }
    }
}

print("max is \(max)")

照旧使用 Xcode 创建 Command Line Tool 类型项目,将源代码贴入 main.swift 文件中,不过这次为我们干苦力的是最新的 Xcode 15 beta。

我们直接切换至 Release 模式,运行结果如下:

% time ./test
max is 991
./test  0.02s user 0.00s system 86% cpu 0.023 total
% time ./test
max is 991
./test  0.02s user 0.00s system 86% cpu 0.024 total
% time ./test
max is 991
./test  0.02s user 0.00s system 89% cpu 0.021 total

可以看到,Swift 优化还是一如既往的棒,只需 0.02 秒多一点。

2.3 C 语言

#include <stdio.h>

int main() {
    int start = 1;
    int end = 63;
    int max = 0;

    for (int a = start; a <= end; a++) {
        for (int b = start; b <= end; b++) {
            for (int c = start; c <= end; c++) {
                for (int d = start; d < end; d++) {
                    if(a + b + c + d == 63) {
                        int rlt = a*b + b*c + c*d;
                        if(rlt >= max) {
                            max = rlt;
                        }
                    }
                }
            }
        }
    }

    printf("max is %d\n", max);
    return 0;
}

同样,我们使用clang 的 O2 优化选项来编译 C 代码,耗时稍微比 Swift 慢一丢丢:

% time ./test
max is 991
./test  0.02s user 0.00s system 93% cpu 0.027 total
% time ./test
max is 991
./test  0.02s user 0.00s system 93% cpu 0.027 total
% time ./test
max is 991
./test  0.02s user 0.00s system 93% cpu 0.027 total

ARM64 汇编

因为无论是 C、Swift 还是 Ruby 的源代码都可以“原封不动”的在 intel 或 M2 芯片上编译执行,所以它们的兼容性非常高!

不过,汇编语言可就“拖后腿”了。

x64 汇编是无法直接在 M2 上执行的。于是乎,我们呼唤 M2 芯片原生指令集汇编火速抵达战场。


x64 汇编生成的可执行文件能不能通过 Rosetta2 在 M2 上执行呢?这是一个有趣的问题,有条件的小伙伴们可以亲自测试一下。😉


所以现在,轮到我们主角 ARM64 闪亮登场了。

Apple Silicon(M1,M2等)是苹果公司开发的基于 ARM 架构的系列芯片,用于替代之前使用的英特尔芯片,旨在提高 Mac 电脑的性能和能效。

Apple Silicon 芯片兼容 ARMv8 指令集,并以 64 位模式(AArch64)运行。所以,我们可以使用 ARM64 汇编语言来构建 M2 上的可执行文件。

ARM 是 risc 架构的 cpu,它的指令是等长(32位)的,不像 intel 是变长指令集。


关于更多 64 位汇编语言的相关知识,感谢兴趣的小伙伴们可以移步到我的汇编(Asm)专栏中观赏相关文章:

  • 大熊猫侯佩的 Asm 系列专栏

下面的 ARM64 汇编代码在关键处做了详细的注释,大家可以轻松读懂:

# as test_arm64.s -o ta64.o
# ld ta64.o -lSystem -L `xcrun --show-sdk-path -sdk macosx`/usr/lib -o ta64
# test_a64.s

.equ        total, 63

    .text
    .globl  _main
    .p2align    2
_main:
    sub     sp,sp,#32
    stp     x29,x30,[sp,#16]
    add     x29,sp,#16

    mov     w0,1    // a in w0
    mov     w1,w0   // b in w1
    mov     w2,w0   // c in w2
    mov     w3,w0   // d in w3
    mov     w11,wzr // max in w11
start_a_loop:
    cmp     w0,total
    b.hi    end_a_loop
start_b_loop:
    cmp     w1,total
    b.hi    end_b_loop
start_c_loop:
    cmp     w2,total
    b.hi    end_c_loop
start_d_loop:
    cmp     w3,total
    b.hi    end_d_loop
    // 计算 a + b + c + d 的值
    add     w4,w0,w1
    add     w4,w4,w2
    add     w4,w4,w3
    cmp     w4,total
    b.ne    not_equ_63
    // 若等于 a + b + c + d = 63,则计算 ab + bc + cd 的值 x
    mul     w4,w0,w1
    mul     w5,w1,w2
    mul     w6,w2,w3
    add     w5,w5,w6
    add     w4,w4,w5
    // 若 x > max ,则需要更新 max 为 x 值
    cmp     w4,w11
    b.ls    not_equ_63
    mov     w11,w4
not_equ_63:
    add     w3,w3,#1
    b       start_d_loop
end_d_loop:
    mov     w3,wzr
    add     w2,w2,#1
    b start_c_loop
end_c_loop:
    mov     w2,wzr
    add     w1,w1,#1
    b       start_b_loop
end_b_loop:
    mov     w1,wzr
    add     w0,w0,#1
    b       start_a_loop
end_a_loop:
    adr     x0,string
    str     x11,[sp]
    bl      _printf

    ldp     x29,x30,[sp,#16]
    add     sp,sp,#32
    # eor x0,x0,x0
    mov     x0,#0
    ret
string: .asciz  "max is %ld\n"

使用 as 汇编器和 ld 链接器生成可执行文件,那么它的执行速度如何呢?

% time ./ta64
max is 992
./ta64  0.03s user 0.00s system 91% cpu 0.034 total
% time ./ta64
max is 992
./ta64  0.03s user 0.00s system 94% cpu 0.031 total
% time ./ta64
max is 992
./ta64  0.03s user 0.00s system 94% cpu 0.031 total

可能大家会对上面的结果失望了,它比 Swift 和 C 还要慢。

至于是什么原因,我们在之前的博文中已经谈过了。

虽然上面汇编代码运行速度只能排在第 3 名,但它也是可以再被优化的。不过,限于篇幅这里不再展开说明了,有兴趣的童鞋可以关注我的其它博文。

另一种乘法的实现

上面我们在实现 ab + bc + cd 的计算时,使用的是标准的乘法指令: mul。M2 芯片还支持 SIMD (单指令流多数据流)指令集,它们可以在一条指令中同时完成多个数据的计算。

下面,我们就用 SIMD 指令来重写之前 mul 乘法计算的逻辑:

// 其余代码不变,省略之...
start_d_loop:
    cmp     w3,total
    b.hi    end_d_loop
    // 计算 a + b + c + d 的值
    add     w4,w0,w1
    add     w4,w4,w2
    add     w4,w4,w3
    cmp     w4,total
    b.ne    not_equ_63
    // 若等于 a + b + c + d = 63,则计算 ab + bc + cd 的值 x
    // 将 a,b,c 依次放入 v1 SIMD 寄存器的 3 个 32 位通道中
    mov     v1.s[0],w0
    mov     v1.s[1],w1
    mov     v1.s[2],w2
	// 同样将 b,c,d 依次放入 v2 寄存器的对应通道中
    mov     v2.s[0],w1
    mov     v2.s[1],w2
    mov     v2.s[2],w3
	// 用 mul.4s 指令同时计算 a*b,b*c,c*d 的值
    mul.4s  v0,v1,v2
    // 计算 ab + bc + cd 的值,并将结果放入 w4 寄存器
    mov     w4,v0.s[0]
    mov     w5,v0.s[1]
    mov     w6,v0.s[2]
    add     w5,w5,w6
    add     w4,w4,w5
    /* 原来的 mul 指令...
    mul     w4,w0,w1
    mul     w5,w1,w2
    mul     w6,w2,w3
    add     w5,w5,w6
    add     w4,w4,w5
	*/
    // 若 x > max ,则需要更新 max 为 x 值
    cmp     w4,w11
    b.ls    not_equ_63
    mov     w11,w4
not_equ_63:

运行发现,结果并没有太大变化,可能该场景不是 SIMD 指令发挥最大威力的领域:

% time ./ta64
max is 992
./ta64  0.03s user 0.00s system 93% cpu 0.031 total
% time ./ta64
max is 992
./ta64  0.03s user 0.00s system 93% cpu 0.031 total

至此,我们分别用 Ruby、Swift、C、x64 和 ARM64 汇编语言实现了博文开头的算法题,相信大家对于这些语言的语法特点、底层兼容性和速度有了更多的了解。

总结

在本篇博文中,我们用 ARM64 汇编实现了之前题目的算法,并用 SIMD 指令重写了其中对应的乘法(mul)运算操作。

感谢观赏,再会!😎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/628542.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手把手教你如何把系统的前端页面改成vue 脚手架项目,实现前后端分离

这篇文章从零开始&#xff0c;介绍怎么把现有的一个商城的页面移动到vue脚手架项目上&#xff0c;实现前后端代码的完全分离。 首先&#xff0c;我们需要新建一个vue的脚手架的项目&#xff0c;在此之前&#xff0c;需要安装node.js 打开IntelliJ IDEA或者winR&#xff0c;输入…

【Java基础学习打卡03】计算机中数据的表示、存储与处理

目录 前言一、数据的表示1.数据与信息2.计算机中的数据3.计算机中数据的单位 二、数据的存储三、数据的处理1.进位计数值2.进制间转换 四、字符编码总结 前言 本小节主要介绍在计算机中数据的表示、存储与处理。要知道计算机内部使用二进制数据&#xff0c;也就是0和1组成的数…

STM32单片机+机智云AIoT+智能服药箱

摘要:随着我国老龄化进程的日趋加快&#xff0c;越来越多的老年人步入了快乐并充实的晚年生活。但是随着年龄的增长&#xff0c;各种医药用品也成了老年人生活的必需品&#xff0c;有人每天甚至需要在不同时间服用多种不同的药物&#xff0c;如果子女不在身边&#xff0c;老年人…

一款基于RT-Thread操作系统的自动测温+风扇自动调速+加湿+自动启停的智能风扇项目(附源码下载)

基于 RT-Thread 的智能加湿风扇 作品背景 一款基于 RT-Thread 操作系统的自动检测温湿度情况进行风扇转速自动调节&#xff0c;加湿自动启停的桌面风扇。 所用硬件&#xff1a; 主控&#xff1a;CH32V307 开发板。温湿度检测&#xff1a;AHT10 温湿度模块。风扇主体&#x…

企业开源测试项目实战(附全套实战项目教程+视频+源码)

接口测试项目 1. No matching distribution found for itypes1.1.0 Could not find a version that satisfies the requirement itypes1.1.0 (from -r requirements.txt (line 8)) (from versions: ) No matching distribution found for itypes1.1.0 (from -r requirements.…

Deepspeed Chat项目理解

ChatGPT的广泛使用促进大模型火起来了&#xff0c;深度学习人工智能开启了2.0时代&#xff0c;deepspeed chat是微软开源的大模型训练工具&#xff0c;它充分利用了deepspeed的高效训练的特点&#xff0c;能够自动化的进行多种大模型训练。 言归正传&#xff0c;在博客中我将对…

什么是大数据,常见的大数据应用领域?

现在大数据发展的如火如荼&#xff0c;也有不少小伙伴对于什么是大数据比较感兴趣&#xff0c;那么大数据在比较官方的定义是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力…

ASP.NET Core Web API入门:创建新项目

ASP.NET Core Web API入门&#xff1a;创建新项目 一、引言二、创建新项目三、加入Startup类&#xff0c;并替换Program.cs内容四、编辑Program.cs代码五、修改控制器的路由六、运行项目 一、引言 最近闲着&#xff0c;想着没真正从0-1开发过ASP.NET Core Web API的项目&#…

Navicat for Redis 与 Navicat Premium 16.2 现已正式发布 | 释放 Redis 全部潜能

今天&#xff0c;我们正式发布 Navicat for Redis 与 Navicat Premium 16.2 两款产品。注入 Redis 能力&#xff0c;这对 Navicat 具有里程碑意义。 此次首发的 Navicat for Redis 数据库管理开发工具&#xff0c;将为 Redis 用户的日常工作带来更为便捷、高效的全新体验。同时…

XV-442-57CQB-1-10伊顿触摸屏EATON

​ XV-442-57CQB-1-10伊顿触摸屏EATON XV-442-57CQB-1-10伊顿触摸屏EATON plc的通信模块是用来完成与别的PLC&#xff0c;其他智能控制设备或计算机之间的通信。以下简单介绍FX系列通信用功能扩展板、适配器及通信模块。 &#xff08;1&#xff09;通信扩展板FX2N-232-BD…

第十五章物资需求计划

物料需求计划&#xff08;MRP&#xff09;是物料管理&#xff08;MM&#xff09;和生产计划&#xff08;PP&#xff09;的组成部分。在本章中&#xff0c;您将学习如何使用MRP来优化物流和供应链规划流程。 物料需求计划&#xff08;MRP&#xff09;是一种用途广泛、直观的计划…

44 最佳实践-性能最佳实践-裸设备映射

文章目录 44 最佳实践-性能最佳实践-裸设备映射44.1 概述44.2 配置示例 44 最佳实践-性能最佳实践-裸设备映射 44.1 概述 配置虚拟机存储设备时&#xff0c;除了将文件配置给虚拟机作为虚拟磁盘使用外&#xff0c;还可以将块设备&#xff08;物理LUN、逻辑卷等&#xff09;直…

VXLAN技术应用场景及测试

定义 RFC7348定义了VLAN扩展方案VXLAN&#xff08;Virtual eXtensible Local Area Network&#xff0c;虚拟扩展局域网&#xff09;。 VXLAN采用MAC in UDP&#xff08;User Datagram Protocol&#xff09;封装方式&#xff0c;是NVO3&#xff08;Network Virtualization ove…

设计模式(行为型模式)之:Strategy(策略模式)

文章目录 本质&#xff1a;动机&#xff1a;定义&#xff1a;一个不好的例子策略模式重写总结 本质&#xff1a; 分离算法&#xff0c;选择实现。动机&#xff1a; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算…

LeetCode 按摩师 python

目录 1.题目描述 2.普通解法&#xff08;通过部分测试用例&#xff09; ​编辑 3.动态规划解法 3.题目总结 1.题目描述 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻…

(六)CSharp-CSharp图解教程版-委托

一、委托概述 1、什么是委托 委托和类一样&#xff0c;是一种用户定义类型&#xff08;即是一种类&#xff0c;所以也是一个引用类型&#xff09;。在它们组成的结构方面区别是&#xff0c;类表示的是数据和方法的集合&#xff0c;而委托则持有一个或多个方法。 可以把 deleg…

Spring架构篇--2.7.1 远程通信基础--Netty原理--NioEventLoopGroup

前言&#xff1a;在使用Netty 时不管是服务端还是客户端都需要 new NioEventLoopGroup 对象进行工作&#xff0c;NioEventLoopGroup的作用是什么呢&#xff1b; 1 NioEventLoopGroup 类图&#xff1a; 从类名字来看它是一个Nio 流的事件轮询器组&#xff0c;既然是一组顾名思…

使用dataFEED OPC Suite将西门子PLC数据转发至阿里云RDS数据库

一 背景 工业现场级别的各种设备会产生大量的数据&#xff0c;这些数据包含生产过程的各种信息&#xff0c;在经过数据库等IT应用的处理后&#xff0c;可为企业提供全面的生产数据分析和决策支持。以往工厂的数据库通常部署在本地&#xff0c;然而得益于云计算的快速发展以及云…

k8s harbor镜像仓库搭建

1.前言 Harbor 是一个开源的云原生镜像仓库&#xff0c;用于存储和分发 Docker 镜像。它提供了一些安全性和管理方面的功能&#xff0c;使得用户可以更好地管理和共享 Docker 镜像 2.配置harbor搭建环境 harbor的搭建需要用到docker、docker-compose服务 docker搭建参考&am…

Vivado 下 IP核之双端口 RAM 读写

目录 Vivado 下 IP核之双端口 RAM 读写 1、RAM IP 核简介 2、实验任务 3、程序设计 3.1、RAM IP 核配置 3.2、顶层模块设计 &#xff08;1&#xff09;绘制波形图 4、编写代码 4.1、顶层模块 ip_2port_ram 4.2、RAM 写模块设计 4.3、ram_wr 模块代码 4.4、RAM 读模…