a64汇编语言是armv8架构64位汇编语言的缩写,更多的时候被称为Aarch64汇编语言。几年用x64汇编语言实现了256位、384位和521位无符号整数乘法,最近弄了个树莓派4b当玩具,学了三天a64汇编语言,就用a64编程实现了256位无符号整数乘法。老实讲,这个事情并不算什么蛋疼无聊之举,然而可供吐槽之处仍旧远超程序代码本身。
用64位汇编语言编写256位无符号整数乘法非常简单,简单到比C语言还要简单的程度,无论是a64还是x64。编写对应的测试代码(C程序)才是更加需要上心的事情。然后,请对程序运行结果疯狂吐槽吧。
首先是头文件,如果是x64平台,命名为ecc-x64.h,如果是Aarch64平台,则命名为ecc-a64.h,其内容完全相同,就是一条函数声明:
uint64_t mp_mul_256(uint64_t c[8], uint64_t a[4], uint64_t b[4]);
然后是测试自编函数的测试程序,命名为mp_test.c,除了引用上述头文件的文件名不同之外,其它内容完全相同:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include "ecc-a64.h"//when Aarch64
#include "ecc-x64.h"//when x86_64
void hex_out(uint64_t data[], uint64_t size)
{
uint64_t i;
for(i = 0; i < size; i++) fprintf(stdout, "%016lx", data[size - i - 1]);
}
int main(int argc, char *argv[])
{
uint64_t c[8], a[4], b[4];
uint64_t i, j;
//init
a[3] = 0x32C4AE2C1F198119;
a[2] = 0x5F9904466A39C994;
a[1] = 0x8FE30BBFF2660BE1;
a[0] = 0x715A4589334C74C7;
b[3] = 0xBC3736A2F4F6779C;
b[2] = 0x59BDCEE36B692153;
b[1] = 0xD0A9877CC62A4740;
b[0] = 0x02DF32E52139F0A0;
//process
j = 0x8000000;
for(i = 0; i < j; i++) mp_mul_256(c, a, b);
//output
hex_out(c, 8);
exit(EXIT_SUCCESS);
}
只测自己编写的实现代码纯属自嗨,所以还得拉大旗做虎皮,而且得是两只老虎,一个是libGMP,命名为 mpz_test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <gmp.h>
int main(int argc, char *argv[])
{
mpz_t a, b, c;
int i = 0;
//init
mpz_init2(a, 256);
mpz_init2(b, 256);
mpz_init2(c, 512);
//input
mpz_init_set_str(a, "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
mpz_init_set_str(b, "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
//process
for(i = 0; i < 0x8000000; i++) mpz_mul(c, a, b);
//output
mpz_out_str(stdout, 16, c); fprintf(stdout, "\n");
//free
mpz_clear(c);
mpz_clear(b);
mpz_clear(a);
exit(EXIT_SUCCESS);
}
另一个就是OpenSSL,命名为 bn_test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <openssl/bn.h>
char *Gx = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
char *Gy = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
int main(int argc, char *argv[])
{
BIGNUM *a, *b, *c;
BN_CTX *ctx;
char *hex = NULL;
int i = 0;
//init
a = BN_new();
b = BN_new();
c = BN_new();
ctx = BN_CTX_new();
//input
BN_hex2bn(&a, Gx);
BN_hex2bn(&b, Gy);
//process
for(i = 0; i < 0x8000000; i++) BN_mul(c, a, b, ctx);//c = a * b
//output
hex = BN_bn2hex(c);
fprintf(stdout, "%s\r\n", hex);
OPENSSL_free(hex);
//free
BN_free(a);
BN_free(b);
BN_free(c);
BN_CTX_free(ctx);
exit(EXIT_SUCCESS);
}
这三个测试程序干得都是同一件事情:给定两个256位无符号整数,计算其乘积0x8000000次,输出作为结果的512位无符号整数。测试者需要记录对比两种平台各个测试程序的运行耗时,对测试总体结果做出主观评价。
喵喵喵,我是不是忘了什么?对了,Makefile差点忘了贴出来,两个平台的Makefile是一模一样的
all: *.c *.s *.h
gcc -Wall -O2 mp_test.c *.s -o mp_test
gcc -Wall -O2 -lgmp mpz_test.c -o mpz_test
gcc -Wall -O2 -lcrypto bn_test.c -o bn_test
clean:
rm -f mp_test mpz_test bn_test
汇编语言是最美的编程语言,我猜读者一定赞同我的伟大看法,迫不及待地阅读我编写的函数mp_mul_256(c, a, b)的完整源代码了,相信我,阅读双平台汇编语言程序代码有益于睡眠,如果配合《线性代数》和《基础数论》这两本睡前读物,那真的就一觉睡到大天亮了。为了不让阅读本文的读者昏昏欲睡,我决定将完整汇编代码放在【a64 vs x64】用汇编语言编程实现256位无符号整数乘法(中),那里面除了代码真的啥都没有。