在博文(1)和(2)里分别用了4中方式写一个素数筛选的算法,分别是javascript in browser、node.js、ruby和c;最终的结果是c最快,node.js其次,js in b虽然也不慢,但极不稳定,所以排在第三,ruby最慢。

现在我们在linux64中用汇编语言重写sieve算法,看看动用最终的武器:汇编语言,我们能不能进一步优化素数筛选算法。

如果忘了算法逻辑,不要紧,下面分别再次贴出node.js、ruby以及c的sieve代码:

首先是node.js:

function sieve(n){
    var a = new Int8Array(n+1);
    var max = Math.floor(Math.sqrt(n));
    var p = 2;
    while(p <= max){
        for(var i=2*p;i<=n;i+=p)
            a[i] = 1;
        while(a[++p]); /* empty */
    }
    while(a[n]) n--;
    return n;
}

然后是ruby:

def sieve(n)
    a = Array.new(n+1);
    max = Math.sqrt(n).to_i;
    p = 2;
    while p<=max  do
        i = 2*p
        while i<=n do
            a[i] = 1
            i+=p
        end
        while a[p+=1] == 1 do end
    end
    while a[n] do n-=1 end
    n
end

最后是c的代码:

ULL sieve(ULL n)
{
    char *a = malloc(n+1);
    if(!a) return 0;
    memset(a,0,n+1);
    ULL max = sqrtl(n);
    ULL p = 2;
    while(p <= max){
        for(ULL i=2*p;i<=n;i+=p)
            a[i] = 1;
        while(a[++p]); /* empty */
    }
    while(a[n]) n--;
    return n;
}

下面尝试用汇编重写sieve函数,需要注意的几点是:

  1. 可以不调用C库中的sqrtx标准函数,直接使用浮点fsqrt指令;
  2. 可以将绝大部分内存变量放到寄存器中以加速存取;
  3. 只关心sieve函数的算法,而用c代码调用汇编的sieve,这样可以发挥各自的长处;否则我还得写个读取输入参数的前导代码,不值当的;
  4. 注意汇编和c的调用接口:在linux64中,参数并不压栈传递;因为sieve只有一个参数,所以放在rdi中传递,返回值还是放在rax中。
  5. 需要调用mmap申请足够的内存以便做筛表。注意这里没有写足够详细的错误处理,更详细的操作请参考本猫的【linux下64位汇编的系统调用】系列博文。
  6. 最后要注意的是,代码优化和代码编写一定不要同时进行!这在所有编程语言中都适用,汇编中尤为重要!否则必成一锅粥鸟!因为谁都不可能上来就写优化后的代码,一定是先功能逻辑正常后在着手考虑优化的问题。本猫第一遍写的是最保守代码,全部变量放在内存中,随用随取,用完保存。在代码逻辑正确后(这时计算sieve 100000000所花时间为4xxx ms),在逐步将内存变量转放到寄存器中。

要说明的是该段代码肯定还可以进一步优化,但本猫就到这里为止了,希望能够抛砖引玉。先把结果说一下吧:用汇编写的sieve版本是最快的,超过了c代码,在本猫 Intel(R) Core(TM)2 Duo CPU T7100 @ 1.80GHz上跑出了最快的37xx毫秒,比c版的平均要快100-200毫秒,而且非常稳定。

最后贴出C的main.c和汇编的sieve.s代码:

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

typedef unsigned long long ULL;
ULL sieve(ULL n);

int main(int argc,char **argv){
    ULL n = 0;
    if(argc < 2){
        printf("usage %s n\n",argv[0]);
        return 1;
    }
    sscanf(argv[1],"%llu",&n);
    if(n == 0){
        puts("wrong number format");
        return 2;
    }
    else if(n < 0){
        puts("must + number");
        return 3;
    }

    int start = clock();
    ULL result = sieve(n);
    if(result == -1){
        puts("sieve calc failed!");
        return 4;
    }
    double end = ((1.0 * (clock() - start)) / CLOCKS_PER_SEC) * 1000.0;
    printf("max p is %llu (take %f ms)\n",result,end);
    return 0;
}

汇编的sieve.s:

section .data
    n:dq 0
    len:dq 0
    addr: dq 0
    p:dq 2
    max:dq 0
    i:dq 2
section .text
    global sieve
sieve:
    push rbp
    push rbx
    push rcx
    mov rbp,rsp
    mov [n],rdi         ;save 1st arg to n

    inc rdi
    mov [len],rdi           ;mmap len = n + 1
    mov eax,9           ;call syscall mmap
    mov rdi,0
    mov rsi,[len]
    mov rdx,3
    mov r10,33
    mov r8,-1
    mov r9,0
    syscall
    cmp rax,0xfffffffffffff001      ;mmap error
    jb next
    mov rax,-1          ;return -1
    jmp quit
next:   ;save mmap return addr
                    ;FIXME:mmap space always 0 ???
    fild qword [n]          ;calc sqrt(n) and save result to max
    fsqrt
    fistp qword [max]
    mov r15,[p]         ;r15 = p
    mov r14,[max]           ;r14 = max
    mov r13,[n]         ;r13 = n
    mov r12,[i]         ;r12 = i
enter_while:
    cmp r15,r14         ;if p<=max
    ja quit_while
    mov rbx,r15
    shl rbx,1
    mov r12,rbx
enter_for:
    cmp r12,r13
    ja quit_for
    mov byte [rax + r12],1
    add r12,r15
    jmp enter_for
quit_for:
    inc r15
    mov cl,byte [rax + r15]
    test cl,cl
    jnz quit_for

    jmp enter_while
quit_while:
    mov cl,byte [rax + r13]
    test cl,cl
    jz pre_quit
    dec r13
    jmp quit_while
pre_quit:
    mov rax,r13
quit:
    mov rsp,rbp
    pop rcx
    pop rbx
    pop rbp
    ret

javascript、ruby和C性能一瞥(3) :上汇编的更多相关文章

  1. javascript、ruby和C性能一瞥(1)

    测试一下本地js.浏览器中的js以及ruby对于类似算法的性能.结果有些意外:浏览器js最快,本地其次当相差很小:ruby最慢而且不是一个数量级的: 因为写的匆忙,可能有重大问题没能看出来,请各位高人 ...

  2. javascript、ruby和C性能一瞥(2)

    好吧,最后让我们用C来实现,看看再能榨取多少性能.注意我没有改变算法,C的算法和之前的3种都是基本相同的: #include <stdio.h> #include <stdlib.h ...

  3. javascript复制内容到剪切板/网页上的复制按钮的实现

    javascript复制内容到剪切板/网页上的复制按钮的实现:DEMO如下 <!doctype html> <html> <head> <meta chars ...

  4. NVIDIA深度学习Tensor Core性能解析(上)

    NVIDIA深度学习Tensor Core性能解析(上) 本篇将通过多项测试来考验Volta架构,利用各种深度学习框架来了解Tensor Core的性能. 很多时候,深度学习这样的新领域会让人难以理解 ...

  5. Yolov4性能分析(上)

    Yolov4性能分析(上) 一.目录 实验测试 1) 测试介绍 2) Test 3) Train 二.   分析 1.实验测试 1. 1  实验测试方法 Yolov4训练train实验方法(Darkn ...

  6. JavaScript数据存取的性能问题

    JavaScript中四种基本的数据存取位置: 字面量:只代表自身 字符串.数字.布尔值.对象.函数.数组.正则,以及null和undefined    快 本地变量:var定义的    快 数组元素 ...

  7. JavaScript 踩坑心得— 为了高速(上)

    一.前言 很多情况下,产品的设计与开发人员一直想打造一套高品质的解决方案,从而快速.平稳地适应产品迭代.速度是衡量产品适应性的真正且唯一的标准,而且,这并不是笔者的一家之言. 「速度是衡量适应能力的真 ...

  8. JavaScript代码规范和性能整理

    性能 Js在性能方面有多要注意的地方: 避免全局查找 Js性能优化最重要的就是注意全局查找,因为作用域的查找是先找局部作用域在没有找到之后在去上一级作用域查找直到全局作用域,所以全局作用域查找的性能消 ...

  9. (转)Javascript的DOM操作 - 性能优化

    转载:https://my.oschina.net/blogshi/blog/198910 摘要: 想稍微系统的说说对于DOM的操作,把Javascript和jQuery常用操作DOM的内容归纳成思维 ...

随机推荐

  1. NewSQL数据库VoltDB特性简介

    VoltDB是一个革命性的新型数据库产品,被称作NewSQL数据库.它基于H-Store,号称比当前数据库产品的吞吐量高45倍,同时又具有很高的扩展性.它的特性主要有以下几点: Ø  高吞吐.低延迟: ...

  2. 为何不能在viewDidLoad方法中显示其他视图

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 你可以使得当视图控制器(view controller)显示自 ...

  3. Unity插件 - MeshEditor(五) 网格顶点动画(变形动画)

    源码已上传至github,并持续更新,链接请看底部.(本帖跟随github持续更新) 网格顶点动画(变形动画)是针对于物体的形状可以随意变换并记录为关键帧的动画,虽然模型的顶点数据还是应该交给GPU绘 ...

  4. Android简易实战教程--第二话《两种进度条》

    点击按钮模拟进度条下载进度,"下载"完成进度条消失. 代码如下: xml: <?xml version="1.0" encoding="utf- ...

  5. 极光推送---安卓Demo

    对于一个一直干.net的程序媛来说,冷不丁的让小编干安卓,那种感觉就好似小狗狗咬小刺猬一样,不知道从哪儿开始下手,对于小编来说,既是挑战更是机遇,因为知识都是相通的,再者来说,在小编的程序人生中,留下 ...

  6. struts2国际化全例 错误解决

    在struts2中需要做国际化的有: jsp页面的国际化,action错误信息的国际化,转换错误信息的国际化,校验错误信息的国际化 在之前的例子中已经做过和国际化相关的例子了,在struts.xml中 ...

  7. JavaScript 关键字

    JavaScript 关键字 和其他任何编程语言一样,JavaScript 保留了一些关键字为自己所用. JavaScript 同样保留了一些关键字,这些关键字在当前的语言版本中并没有使用,但在以后 ...

  8. pig 的chararry类型不能用比较运算符comparison operator

    pig 的chararry类型可能是按字段,逐个字段进行比较. element_id 是chararray类型, 语句: no_app_category_mapping = filter no_ele ...

  9. JAVA之旅(十一)——RuntimeException,异常的总结,Package,jar包,多线程概述

    JAVA之旅(十一)--RuntimeException,异常的总结,Package,jar包,多程序概述 继续JAVA之旅 一.RuntimeException 在Exception种有一个特殊的子 ...

  10. 使用Libgdx开发的FlappyBird(像素鸟、疯狂的小鸟)游戏源码

    本帖最后由 宋志辉 于 2014-10-21 15:06 编辑 点击进入下载地址 Flappy Bird(飞扬的小鸟)由一位来自越南河内的独立游戏开发者阮哈东开发,是一款形式简易但难度极高的休闲游戏. ...