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位無符號整數乘法(中),那裏面除了代碼真的啥都沒有。