【a64 vs x64】用匯編語言編程實現256位無符號整數乘法(上)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章