金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 前端高性能计算之二,C语言不定参数

前端高性能计算之二,C语言不定参数

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-11-20 14:30

Refers

  • 1 赞 收藏 评论

图片 1

int main(int argc, char *argv[])
{
    Array a;
    int i = 0;
    int j;
    int total = 1;
    array_init(&a, 2, 4, 6);
   
    for(; i < 4; i++)
    {
        for(j = 0; j < 6; j++)
        {
            array_set_value(&a, total++, i, j);
        }
    }

client:Makefile ${WSNAME}.wsdl ${WSNAME}test.c $(ALL_OBJS) stdsoap2.o
        $(CC) ${WSNAME}test.o $(CLIENT_OBJS) -o ${WSNAME}test

最近,遇到一个c语言的不定参数问题。其实,对于c语言的不定参数问题,只需要三个函数就可以搞定了。这三个函数的头文件是<stdarg.h>,其实下面的三个函数都是一个宏定义(macro)。
    这三个函数是:
    void va_start(va_list ap, last);
    type va_arg(va_list ap, type);
    void va_end(va_list ap);
    如果需要进行其他的一些操作,可以查看一下man手册进行查询。
    在这三个函数解释之前,先看一个变量va_list,这个变量的类型是什么呢?通过查看内核源代码,一直追踪下去,才发现它的类型是void *类型的。
    对于va_start(va_list ap, last)函数,这个函数是用来初始化指针变量ap(va_list类型)的,以后处理参数就是默认从ap处开始处理。last一般为char *传过来参数列表的第一个参数。
    对于va_arg(va_list ap, type)函数来说,就是将ap指针按照type类型向后移动,然后取出ap指针所指的那个参数。
    对于va_end(va_list ap)一般和va_start(va_list ap, last)配套使用,做一些善后处理的事情。
    这里有一个问题,当我们取参数的时候,如何判断我们要取的参数已经取完了?开始我是这么想的,通过va_arg的返回值进行判断,通过查阅资料,都是这么说的,看来我的猜想是对的。当我把程序写出来进行测试的时候,发现不是这样的:
#include <stdio.h>
#include <stdarg.h>
int sum(const int , ...);
int main(void)
{
    printf("The result is:%d/n", sum(10, 9, 8));
    return 0;
}
int sum(const int first, ...)
{
    va_list argp;
    int sum = 0;
    int tmp;
    va_start(argp, first);
    sum += first;
    printf("%d/n", first);
    while((tmp = va_arg(argp, int)) != 0) {
        printf("%d/n", tmp);
        sum += tmp;
    }
    va_end(argp);
    return sum;
}
这个程序的运行结果是:
10
9
8
6676468
134513824
The result is:141190319
    这个结果说明,通过va_arg的返回值进行参数是否取完来判断是有问题的。
    会不会是通过argp的值来判断的呢?让我们来做个测试:
#include <stdio.h>
#include <stdarg.h>
int sum(const int , ...);
int main(void)
{
    printf("The result is:%d/n", sum(10, 9, 8));
    return 0;
}
int sum(const int first, ...)
{
    va_list argp;
    int sum = 0;
    int tmp;
    va_start(argp, first);
    sum += first;
    printf("%d/n", first);
    while(argp) {
        tmp = va_arg(argp, int);
        printf("%d/n", tmp);
        sum += tmp;
    }
    va_end(argp);
    return sum;
}
    这个程序的执行结果出乎我的意料,出现了段错误。
    至于如何修改这个程序把不定参数取出来,我还是没有找到解决方法。后来,我想到了printf()函数,我查看了它的源代码,其中主要是调用了vsprintf()函数,至于为什么调用vsprintf()函数,我想可能是为了实现类似于fprintf()之类的函数调用的方便,这样也提高了函数的利用率。printf()函数的主要代码:
328 va_start(args, fmt);
329 n = vsprintf(sprint_buf, fmt, args);
330 va_end(args);
    我继续查看了vsprintf()函数,结果发现,在这个函数当中,它好像是通过判断字符串当中“%”号的多少来决定后面参数的个数的。想到这里,我断定,在想调用不定参数这样的函数的时候,其实是需要指出参数的个数的,只是是通过间接的方式。比如我们最熟悉的printf()函数,其实我们在第一个参数当中,通过%号已经指出了参数的个数,不是吗?
    想到这里,我想到了之前看到man手册中给出的例子为什么是这样的:
#include <stdio.h>
       #include <stdarg.h>
       void
       foo(char *fmt, ...)
       {
           va_list ap;
           int d;
           char c, *s;
           va_start(ap, fmt);
           while (*fmt)
               switch (*fmt++) {
               case 's': /* string */
                   s = va_arg(ap, char *);
                   printf("string %s/n", s);
                   break;
               case 'd': /* int */
                   d = va_arg(ap, int);
                   printf("int %d/n", d);
                   break;
               case 'c': /* char */
                   /* need a cast here since va_arg only
                      takes fully promoted types */
                   c = (char) va_arg(ap, int);
                   printf("char %c/n", c);
                   break;
               }
           va_end(ap);
       }
    这里的话,不是就通过第一个参数指定之后才读取的吗?其实我觉得是间接的告诉了参数的个数。
    通过上面的分析,下面做了一个简单的不定参数的应用。
    问题描述:给定一些字符串,求出它们的最长开始字串。
    实验代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
void fun(char *fmt, ...);
int main()
{
    fun("sss", "fanabcd", "fanfanfanfan", "fanyyyyyyyyyyyy");//sss 表示了不定参数的个数
    return 0;    
}
void fun(char *fmt, ...)
{
    va_list argp;
    char * str, res[20] = {0};
    int i;
    va_start(argp, fmt);
    if(*fmt == 's') {
        str = va_arg(argp, char *);
        strcpy(res, str);
    }
    fmt++;
    while(*fmt) {
        if(*fmt++ == 's') {
            str = va_arg(argp, char *);
            i = 0;
            while(res[i] != '/0') {
                if(res[i] != str[i]) {
                    res[i] = 0;
                    break;
                }
                i++;
            }
        }
    }
    va_end(argp);
    printf("The result is:%s/n", res);
}
    程序的执行结果是:
The result is:fan
    通过这样的折腾,就把c语言的不定参数简单地应用起来了

调用C/C++函数

前面的Hello, WebAssembly!都是main函数直接打出来的,而我们使用WebAssembly的目的是为了高性能计算,做法多半是用C/C++实现某个函数进行耗时的计算,然后编译成wasm,暴露给js去调用。

在文件add.c中写如下代码:

JavaScript

#include <stdio.h> int add(int a, int b) { return a + b; } int main() { printf("a + b: %d", add(1, 2)); return 0; }

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}

有两种方法可以把add方法暴露出来给js调用。

    a->ptr[result] = value;
    return 0;
}

比较关键,如果头文件中的 // 部分有修改,要检查是否要修改Makefile文件

通过命令行参数暴露API

JavaScript

emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.js

1
emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.js

注意方法名add前必须加_。 然后我们可以在Node.js里面这样使用:

JavaScript

// file node-add.js const add_module = require('./add.js'); console.log(add_module.ccall('add', 'number', ['number', 'number'], [2, 3]));

1
2
3
// file node-add.js
const add_module = require('./add.js');
console.log(add_module.ccall('add', 'number', ['number', 'number'], [2, 3]));

执行node node-add.js会输出5。 如果需要在web页面使用的话,执行:

JavaScript

emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.html

1
emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.html

然后在生成的add.html中加入如下代码:

JavaScript

<button onclick="nativeAdd()">click</button> <script type='text/javascript'> function nativeAdd() { const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]); alert(result); } </script>

1
2
3
4
5
6
7
  <button onclick="nativeAdd()">click</button>
  <script type='text/javascript'>
    function nativeAdd() {
      const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]);
      alert(result);
    }
  </script>

然后点击button,就可以看到执行结果了。

Module.ccall会直接调用C/C++代码的方法,更通用的场景是我们获取到一个包装过的函数,可以在js里面反复调用,这需要用Module.cwrap,具体细节可以参看文档。

JavaScript

const cAdd = add_module.cwrap('add', 'number', ['number', 'number']); console.log(cAdd(2, 3)); console.log(cAdd(2, 4));

1
2
3
const cAdd = add_module.cwrap('add', 'number', ['number', 'number']);
console.log(cAdd(2, 3));
console.log(cAdd(2, 4));

int array_set_value(Array *a, int value, ...)
{
    if(NULL == a) return -1;
    va_list va;
    va_start(va, value);

 

用asm.js和WebAssembly执行耗时计算

前面准备工作都做完了, 现在我们来试一下用C代码来优化前一篇中提过的问题。代码很简单:

JavaScript

// file sum.c #include <stdio.h> // #include <emscripten.h> long sum(long start, long end) { long total = 0; for (long i = start; i <= end; i += 3) { total += i; } for (long i = start; i <= end; i += 3) { total -= i; } return total; } int main() { printf("sum(0, 1000000000): %ld", sum(0, 1000000000)); // emscripten_exit_with_live_runtime(); return 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// file sum.c
#include <stdio.h>
// #include <emscripten.h>
 
long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 3) {
    total += i;
  }
  for (long i = start; i <= end; i += 3) {
    total -= i;
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 1000000000));
  // emscripten_exit_with_live_runtime();
  return 0;
}

注意用gcc编译的时候需要把跟emscriten相关的两行代码注释掉,否则编译不过。 我们先直接用gcc编译成native code看看代码运行多块呢?

JavaScript

➜ webasm-study gcc sum.c ➜ webasm-study time ./a.out sum(0, 1000000000): 0./a.out 5.70s user 0.02s system 99% cpu 5.746 total ➜ webasm-study gcc -O1 sum.c ➜ webasm-study time ./a.out sum(0, 1000000000): 0./a.out 0.00s user 0.00s system 64% cpu 0.003 total ➜ webasm-study gcc -O2 sum.c ➜ webasm-study time ./a.out sum(0, 1000000000): 0./a.out 0.00s user 0.00s system 64% cpu 0.003 total

1
2
3
4
5
6
7
8
9
➜  webasm-study gcc sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  5.70s user 0.02s system 99% cpu 5.746 total
➜  webasm-study gcc -O1 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.00s user 0.00s system 64% cpu 0.003 total
➜  webasm-study gcc -O2 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.00s user 0.00s system 64% cpu 0.003 total

可以看到有没有优化差别还是很大的,优化过的代码执行时间是3ms!。really?仔细想想,我for循环了10亿次啊,每次for执行大概是两次加法,两次赋值,一次比较,而我总共做了两次for循环,也就是说至少是100亿次操作,而我的mac pro是2.5 GHz Intel Core i7,所以1s应该也就执行25亿次CPU指令操作吧,怎么可能逆天到这种程度,肯定是哪里错了。想起之前看到的一篇rust测试性能的文章,说rust直接在编译的时候算出了答案, 然后把结果直接写到了编译出来的代码里, 不知道gcc是不是也做了类似的事情。在知乎上GCC中-O1 -O2 -O3 优化的原理是什么?这篇文章里, 还真有loop-invariant code motion(LICM)针对for的优化,所以我把代码增加了一些if判断,希望能“糊弄”得了gcc的优化。

JavaScript

#include <stdio.h> // #include <emscripten.h> // long EMSCRIPTEN_KEEPALIVE sum(long start, long end) { long sum(long start, long end) { long total = 0; for (long i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total += i; } else if (i % 5 == 0 || i % 7 == 1) { total += i / 2; } } for (long i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total -= i; } else if (i % 5 == 0 || i % 7 == 1) { total -= i / 2; } } return total; } int main() { printf("sum(0, 1000000000): %ld", sum(0, 100000000)); // emscripten_exit_with_live_runtime(); return 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
// #include <emscripten.h>
 
// long EMSCRIPTEN_KEEPALIVE sum(long start, long end) {
long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total += i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total += i / 2;
    }
  }
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total -= i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total -= i / 2;
    }
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 100000000));
  // emscripten_exit_with_live_runtime();
  return 0;
}

执行结果大概要正常一些了。

JavaScript

➜ webasm-study gcc -O2 sum.c ➜ webasm-study time ./a.out sum(0, 1000000000): 0./a.out 0.32s user 0.00s system 99% cpu 0.324 total

1
2
3
➜  webasm-study gcc -O2 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.32s user 0.00s system 99% cpu 0.324 total

ok,我们来编译成asm.js了。

JavaScript

#include <stdio.h> #include <emscripten.h> long EMSCRIPTEN_KEEPALIVE sum(long start, long end) { // long sum(long start, long end) { long total = 0; for (long i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total += i; } else if (i % 5 == 0 || i % 7 == 1) { total += i / 2; } } for (long i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total -= i; } else if (i % 5 == 0 || i % 7 == 1) { total -= i / 2; } } return total; } int main() { printf("sum(0, 1000000000): %ld", sum(0, 100000000)); emscripten_exit_with_live_runtime(); return 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <emscripten.h>
 
long EMSCRIPTEN_KEEPALIVE sum(long start, long end) {
// long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total += i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total += i / 2;
    }
  }
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total -= i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total -= i / 2;
    }
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 100000000));
  emscripten_exit_with_live_runtime();
  return 0;
}

执行

JavaScript

emcc sum.c -o sum.html

1
emcc sum.c -o sum.html

然后在sum.html中添加代码``

JavaScript

<button onclick="nativeSum()">NativeSum</button> <button onclick="jsSumCalc()">JSSum</button> <script type='text/javascript'> function nativeSum() { t1 = Date.now(); const result = Module.ccall('sum', 'number', ['number', 'number'], [0, 100000000]); t2 = Date.now(); console.log(`result: ${result}, cost time: ${t2 - t1}`); } </script> <script type='text/javascript'> function jsSum(start, end) { let total = 0; for (let i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total += i; } else if (i % 5 == 0 || i % 7 == 1) { total += i / 2; } } for (let i = start; i <= end; i += 1) { if (i % 2 == 0 || i % 3 == 1) { total -= i; } else if (i % 5 == 0 || i % 7 == 1) { total -= i / 2; } } return total; } function jsSumCalc() { const N = 100000000;// 总次数1亿 t1 = Date.now(); result = jsSum(0, N); t2 = Date.now(); console.log(`result: ${result}, cost time: ${t2 - t1}`); } </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<button onclick="nativeSum()">NativeSum</button>
  <button onclick="jsSumCalc()">JSSum</button>
  <script type='text/javascript'>
    function nativeSum() {
      t1 = Date.now();
      const result = Module.ccall('sum', 'number', ['number', 'number'], [0, 100000000]);
      t2 = Date.now();
      console.log(`result: ${result}, cost time: ${t2 - t1}`);
    }
  </script>
  <script type='text/javascript'>
    function jsSum(start, end) {
      let total = 0;
      for (let i = start; i <= end; i += 1) {
        if (i % 2 == 0 || i % 3 == 1) {
          total += i;
        } else if (i % 5 == 0 || i % 7 == 1) {
          total += i / 2;
        }
      }
      for (let i = start; i <= end; i += 1) {
        if (i % 2 == 0 || i % 3 == 1) {
          total -= i;
        } else if (i % 5 == 0 || i % 7 == 1) {
          total -= i / 2;
        }
      }
 
      return total;
    }
    function jsSumCalc() {
      const N = 100000000;// 总次数1亿
      t1 = Date.now();
      result = jsSum(0, N);
      t2 = Date.now();
      console.log(`result: ${result}, cost time: ${t2 - t1}`);
    }
  </script>

另外,我们修改成编译成WebAssembly看看效果呢?

JavaScript

emcc sum.c -o sum.js -s WASM=1

1
emcc sum.c -o sum.js -s WASM=1
Browser webassembly asm.js js
Chrome61 1300ms 600ms 3300ms
Firefox55 600ms 800ms 700ms
Safari9.1 不支持 2800ms 因不支持ES6我懒得改写没测试

感觉Firefox有点不合理啊, 默认的JS太强了吧。然后觉得webassembly也没有特别强啊,突然发现emcc编译的时候没有指定优化选项-O2。再来一次:``

JavaScript

emcc -O2 sum.c -o sum.js # for asm.js emcc -O2 sum.c -o sum.js -s WASM=1 # for webassembly

1
2
emcc -O2 sum.c -o sum.js # for asm.js
emcc -O2 sum.c -o sum.js -s WASM=1 # for webassembly
Browser webassembly -O2 asm.js -O2 js
Chrome61 1300ms 600ms 3300ms
Firefox55 650ms 630ms 700ms

居然没什么变化, 大失所望。号称asm.js可以达到native的50%速度么,这个倒是好像达到了。但是今年Compiling for the Web with WebAssembly (Google I/O ‘17)里说WebAssembly是1.2x slower than native code,感觉不对呢。asm.js还有一个好处是,它就是js,所以即使浏览器不支持,也能当成不同的js执行,只是没有加速效果。当然WebAssembly受到各大厂商一致推崇,作为一个新的标准,肯定前景会更好,期待会有更好的表现。

    int result = array_get_locate(a, va);
    if( -1 == result) return -1;

all:server

安装Emscripten

访问

  1. 下载对应平台版本的SDK

  2. 通过emsdk获取最新版工具

JavaScript

# Fetch the latest registry of available tools. ./emsdk update # Download and install the latest SDK tools. ./emsdk install latest # Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file) ./emsdk activate latest # Activate PATH and other environment variables in the current terminal source ./emsdk_env.sh

1
2
3
4
5
6
7
8
9
10
11
# Fetch the latest registry of available tools.
./emsdk update
 
# Download and install the latest SDK tools.
./emsdk install latest
 
# Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file)
./emsdk activate latest
 
# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh
  1. 将下列添加到环境变量PATH中

JavaScript

~/emsdk-portable ~/emsdk-portable/clang/fastcomp/build_incoming_64/bin ~/emsdk-portable/emscripten/incoming

1
2
3
~/emsdk-portable
~/emsdk-portable/clang/fastcomp/build_incoming_64/bin
~/emsdk-portable/emscripten/incoming
  1. 其他

我在执行的时候碰到报错说LLVM版本不对,后来参考文档配置了LLVM_ROOT变量就好了,如果你没有遇到问题,可以忽略。

JavaScript

LLVM_ROOT = os.path.expanduser(os.getenv('LLVM', '/home/ubuntu/a-path/emscripten-fastcomp/build/bin'))

1
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM', '/home/ubuntu/a-path/emscripten-fastcomp/build/bin'))
  1. 验证是否安装好

执行emcc -v,如果安装好会出现如下信息:

JavaScript

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.21 clang version 4.0.0 ( 974b55fd84ca447c4297fc3b00cefb6394571d18) ( 9e4ee9a67c3b67239bd1438e31263e2e86653db5) (emscripten 1.37.21 : 1.37.21) Target: x86_64-apple-darwin15.5.0 Thread model: posix InstalledDir: /Users/magicly/emsdk-portable/clang/fastcomp/build_incoming_64/bin INFO:root:(Emscripten: Running sanity checks)

1
2
3
4
5
6
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.21
clang version 4.0.0 (https://github.com/kripken/emscripten-fastcomp-clang.git 974b55fd84ca447c4297fc3b00cefb6394571d18) (https://github.com/kripken/emscripten-fastcomp.git 9e4ee9a67c3b67239bd1438e31263e2e86653db5) (emscripten 1.37.21 : 1.37.21)
Target: x86_64-apple-darwin15.5.0
Thread model: posix
InstalledDir: /Users/magicly/emsdk-portable/clang/fastcomp/build_incoming_64/bin
INFO:root:(Emscripten: Running sanity checks)

    a->ptr = (int *) malloc(total * sizeof(int));

        return 0;
}

前端高性能计算之二:asm.js & webassembly

2017/10/21 · HTML5 · webassembly

原文出处: magicly   

前一篇我们说了要解决高性能计算的两个方法,一个是并发用WebWorkers,另一个就是用更底层的静态语言。

2012年,Mozilla的工程师Alon Zakai在研究LLVM编译器时突发奇想:能不能把C/C++编译成Javascript,并且尽量达到Native代码的速度呢?于是他开发了Emscripten编译器,用于将C/C++代码编译成Javascript的一个子集asm.js,性能差不多是原生代码的50%。大家可以看看这个PPT。

之后Google开发了Portable Native Client,也是一种能让浏览器运行C/C++代码的技术。 后来估计大家都觉得各搞各的不行啊,居然Google, Microsoft, Mozilla, Apple等几家大公司一起合作开发了一个面向Web的通用二进制和文本格式的项目,那就是WebAssembly,官网上的介绍是:

WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.

WebAssembly is currently being designed as an open standard by a W3C Community Group that includes representatives from all major browsers.

所以,WebAssembly应该是一个前景很好的项目。我们可以看一下目前浏览器的支持情况: 图片 2

    va_list ap;
    int i;
    long total = 1;

 

Rust

本来还想写Rust编译成WebAssembly的,不过感觉本文已经太长了, 后期再写如果结合Rust做WebAssembly吧。

着急的可以先看看这两篇

  • Compiling to the web with Rust and emscripten
  • Rust ⇋ JavaScript

int array_print_line(Array *a)
{
    int total = 1;
    int i = 0;
    int line ;
    for(; i < a->dim; i++)
    {
        total *= a->bounds[i];
    }
   
    line = total/a->bounds[0];
    for(i = 0; i < total; i++)
    {
        if(0 == i % line && 0 != i) printf("n");
        printf("%-7d", a->ptr[i]);
    }
    printf("n");
    return 0;
}

        soap_end(&SmsWBS_soap);
        soap_done(&SmsWBS_soap);

Hello, WebAssembly!

创建一个文件hello.c

JavaScript

#include <stdio.h> int main() { printf("Hello, WebAssembly!n"); return 0; }

1
2
3
4
5
#include <stdio.h>
int main() {
  printf("Hello, WebAssembly!n");
  return 0;
}

编译C/C++代码:

JavaScript

emcc hello.c

1
emcc hello.c

上述命令会生成一个a.out.js文件,我们可以直接用Node.js执行:

JavaScript

node a.out.js

1
node a.out.js

输出

JavaScript

Hello, WebAssembly!

1
Hello, WebAssembly!

为了让代码运行在网页里面,执行下面命令会生成hello.htmlhello.js两个文件,其中hello.jsa.out.js内容是完全一样的。

emcc hello.c -o hello.html<code>

1
2
emcc hello.c -o hello.html<code>
 

JavaScript

➜ webasm-study md5 a.out.js MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429 ➜ webasm-study md5 hello.js MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429

1
2
3
4
➜  webasm-study md5 a.out.js
MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429
➜  webasm-study md5 hello.js
MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429

然后在浏览器打开hello.html,可以看到页面 图片 3

前面生成的代码都是asm.js,毕竟Emscripten是人家作者Alon Zakai最早用来生成asm.js的,默认输出asm.js也就不足为奇了。当然,可以通过option生成wasm,会生成三个文件:hello-wasm.html, hello-wasm.js, hello-wasm.wasm

JavaScript

emcc hello.c -s WASM=1 -o hello-wasm.html

1
emcc hello.c -s WASM=1 -o hello-wasm.html

然后浏览器打开hello-wasm.html,发现报错TypeError: Failed to fetch。原因是wasm文件是通过XHR异步加载的,用file:////访问会报错,所以我们需要启一个服务器。

JavaScript

npm install -g serve serve

1
2
npm install -g serve
serve

然后访问http://localhost:5000/hello-wasm.html,就可以看到正常结果了。

    array_print_line(&a);
    for(i = 0; i < 4; i++)
    {
        for(j = 0; j < 6; j++)
        {
            printf("%-7d",array_get_value(&a,  i, j));
        }
        printf("n");
    }
    array_destory(&a);
}

int main(int argc, char **argv)
{
        int m, s;               /* master and slave sockets */
        struct soap SmsWBS_soap;

定义函数的时候添加EMSCRIPTEN_KEEPALIVE

添加文件add2.c

JavaScript

#include <stdio.h> #include <emscripten.h> int EMSCRIPTEN_KEEPALIVE add(int a, int b) { return a + b; } int main() { printf("a + b: %d", add(1, 2)); return 0; }

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <emscripten.h>
 
int EMSCRIPTEN_KEEPALIVE add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}

执行命令:

JavaScript

emcc add2.c -o add2.html

1
emcc add2.c -o add2.html

同样在add2.html中添加代码:

JavaScript

<button onclick="nativeAdd()">click</button> <script type='text/javascript'> function nativeAdd() { const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]); alert(result); } </script>

1
2
3
4
5
6
7
  <button onclick="nativeAdd()">click</button>
  <script type='text/javascript'>
    function nativeAdd() {
      const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]);
      alert(result);
    }
  </script>

但是,当你点击button的时候,报错:

JavaScript

Assertion failed: the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)

1
Assertion failed: the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)

可以通过在main()中添加emscripten_exit_with_live_runtime()解决:

JavaScript

#include <stdio.h> #include <emscripten.h> int EMSCRIPTEN_KEEPALIVE add(int a, int b) { return a + b; } int main() { printf("a + b: %d", add(1, 2)); emscripten_exit_with_live_runtime(); return 0; }

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <emscripten.h>
 
int EMSCRIPTEN_KEEPALIVE add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  emscripten_exit_with_live_runtime();
  return 0;
}

或者也可以直接在命令行中添加-s NO_EXIT_RUNTIME=1来解决,

JavaScript

emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=1

1
emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=1

不过会报一个警告:

JavaScript

exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)

1
exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)

所以建议采用第一种方法。

上述生成的代码都是asm.js,只需要在编译参数中添加-s WASM=1中就可以生成wasm,然后使用方法都一样。

    a->base_add = (int *) malloc(dim * sizeof(int));
    a->base_add[dim -1] = 1;
    i = dim -2;
    for(; i >= 0; i--)
    {
        a->base_add[i] = a->base_add[i+1] * a->bounds[i+1];
    }

make client                            ---得到客户端程序SmsWBStest

void array_destory(Array *a)
{
    FREE(a->ptr);
    FREE(a->bounds);
    FREE(a->base_add);
}

 

下面为代码实现。(以下代码是我在ubuntu中简单测试通过。如在别的系统中无法运行,请谅解。本代码只经过简单的测试,如果出现问题,请谅解。)
[plain]
/* 
*created by Reage at 2013 March 28 
*description: 数组的实现,包含创建、赋值、访问、打印 

*blog: 
*/ 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdarg.h> 
 
 
#define MAXDIM 4 
 
typedef struct Array 

    int dim; 
    int *ptr; 
    int *bounds; 
    int *base_add; 
}Array; 
 
int array_init(Array *a, int dim, ...); 
int array_set_value(Array *a, int value, ...); 
int array_print_line(Array *a); 
int array_get_value(Array *a, ...); 
void array_destory(Array *a); 
 
int main(int argc, char *argv[]) 

    Array a; 
    int i = 0; 
    int j; 
    int total = 1; 
    array_init(&a, 2, 4, 6); 
     
    for(; i < 4; i++) 
    { 
        for(j = 0; j < 6; j++) 
        { 
            array_set_value(&a, total++, i, j); 
        } 
    } 
 
    array_print_line(&a); 
    for(i = 0; i < 4; i++) 
    { 
        for(j = 0; j < 6; j++) 
        { 
            printf("%-7d",array_get_value(&a,  i, j)); 
        } 
        printf("n"); 
    } 
    array_destory(&a); 

 
int array_init(Array * a, int dim, ...) 

    if(1 > dim || 8 < dim) 
        return -1; 
    a->dim = dim; 
 
    va_list ap; 
    int i; 
    long total = 1; 
 
    a->bounds = (int *)malloc(dim * sizeof(int)); 
     
    va_start(ap, dim); 
    for(i = 0; i < dim; i++) 
    { 
        a->bounds[i] = va_arg(ap, int); 
        total *= a->bounds[i]; 
    } 
    va_end(ap); 
 
    a->ptr = (int *) malloc(total * sizeof(int)); 
 
    a->base_add = (int *) malloc(dim * sizeof(int)); 
    a->base_add[dim -1] = 1; 
    i = dim -2; 
    for(; i >= 0; i--) 
    { 
        a->base_add[i] = a->base_add[i+1] * a->bounds[i+1]; 
    } 
 
    return 0; 

 
#define FREE(x) if(NULL != (x)) free(x) 
 
void array_destory(Array *a) 

    FREE(a->ptr); 
    FREE(a->bounds); 
    FREE(a->base_add); 

 
int array_get_value(Array *a, ...) 

    va_list va; 
    va_start(va, a); 
     
    int result = array_get_locate(a, va); 
    if(-1 == result) return -1; 
    return a->ptr[result]; 

 
 
int array_print_line(Array *a) 

    int total = 1; 
    int i = 0; 
    int line ; 
    for(; i < a->dim; i++) 
    { 
        total *= a->bounds[i]; 
    } 
     
    line = total/a->bounds[0]; 
    for(i = 0; i < total; i++) 
    { 
        if(0 == i % line && 0 != i) printf("n"); 
        printf("%-7d", a->ptr[i]); 
    } 
    printf("n"); 
    return 0; 

 
 
int array_get_locate(Array *a, va_list va) 

    int result = 0; 
    int bound; 
    int i; 
    for(i = 0; i < a->dim; i++) 
    { 
        bound = va_arg(va, int); 
        if(0 > bound || bound > a->bounds[i]) 
        { 
            return -1; 
        } 
        result += bound * a->base_add[i]; 
    } 
    return result; 

 
int array_set_value(Array *a, int value, ...) 

    if(NULL == a) return -1; 
    va_list va; 
    va_start(va, value); 
 
    int result = array_get_locate(a, va); 
    if( -1 == result) return -1; 
 
    a->ptr[result] = value; 
    return 0; 

int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
{
        *sum = num1 + num2;
        return 0;
}

int array_get_locate(Array *a, va_list va)
{
    int result = 0;
    int bound;
    int i;
    for(i = 0; i < a->dim; i++)
    {
        bound = va_arg(va, int);
        if(0 > bound || bound > a->bounds[i])
        {
            return -1;
        }
        result += bound * a->base_add[i];
    }
    return result;
}

(2)Makefile文件:

   数组一旦被定义了,它的维数和维界就不会变。因此,除了结构的初始化和销毁之外,数组只有存取元素和修改元素。数组一般分为行序和列许。一般系统都是用的行许。
以2维数组a[m][n]为例
列序:
a[0][0]  a[1][0] ...... a[m-1][0]
.............
a[m-1][0] a[m-1][1] ...... a[m-1][n-1]

出来类似下面的显示就表示运行正常

int array_init(Array * a, int dim, ...)
{
    if(1 > dim || 8 < dim)
        return -1;
    a->dim = dim;

int add(const char *server, int num1, int num2, int *sum);

 

#include "soapH.h"
#include "SmsWBS.nsmap"

#define MAXDIM 4

//gsoap ns service name: SmsWBS
//gsoap ns service style: rpc
//gsoap ns service namespace: http://192.168.2.161:8000/SmsWBS.wsdl
//gsoap ns service location: http://192.168.2.161:8000
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:SmsWBS

    return 0;
}

67 + 78 = 145

...

        soap_call_ns__add(&SmsWBS_soap, server, "", num1, num2, sum);

/*
*created by Reage at 2013 March 28
*description: 数组的实现,包含创建、赋值、访问、打印
*
*blog:
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int main(int argc, char **argv)
{
        int result = -1;
        char* server="http://localhost:8000";

#define FREE(x) if(NULL != (x)) free(x)

       要开发的Web Service功能非常简单,就是一个add函数,将两个参数相加,返回其和。

    a->bounds = (int *)malloc(dim * sizeof(int));
   
    va_start(ap, dim);
    for(i = 0; i < dim; i++)
    {
        a->bounds[i] = va_arg(ap, int);
        total *= a->bounds[i];
    }
    va_end(ap);

(1)头文件:SmsWBS.h,注释部分不可少,url部分的IP必须填写当前Linux电脑的IP

行序列:
a[0][0]  a[1][0] ...... a[m-1][0]
.............
a[0][n-1] a[1][ [n-1 ] ...... a[m-1][n-1]
数据一般使用连续的地址存储。计算的方法为 (i, j) = &a + (b) + j;//b 一维元素的个数
推广到多维数组中(j1, j2, j3,.....,jn) = (b2*b3*...*bn * j1 + b3*b4*...*bn*j2 + .....+bn *jn-1 + jn)
例如:已知数组a[5][4][10]。求a[2][3][4] 的地址;
j1=2、j2 = 3、j3 = 4;b1=5、b2=4、b3=10;
a[2][3][4]=b2*b3*j1+b3*j2+j3=4*10*2+10*3+4;

       }

int array_init(Array *a, int dim, ...);
int array_set_value(Array *a, int value, ...);
int array_print_line(Array *a);
int array_get_value(Array *a, ...);
void array_destory(Array *a);

make                                     ---得到服务端程序SmsWBSserver

int array_get_value(Array *a, ...)
{
    va_list va;
    va_start(va, a);
   
    int result = array_get_locate(a, va);
    if(-1 == result) return -1;
    return a->ptr[result];
}

                        fprintf(stderr, "Socket connection successful: slave socket = %dn", s);
                        soap_serve(&SmsWBS_soap);
                        soap_end(&SmsWBS_soap);
                }

typedef struct Array
{
    int dim;
    int *ptr;
    int *bounds;
    int *base_add;
}Array;

        soap_init(&SmsWBS_soap);

int add(const char *server, int num1, int num2, int *sum)
{
        struct soap SmsWBS_soap;
        int result = 0;

        return 0;
}

        if( argc < 3 )
        {
                printf("usage: %s num1 num2 n", argv[0]);
                exit(0);

        if(SmsWBS_soap.error)
        {
                printf("soap error:%d, %s, %s ", SmsWBS_soap.error, *soap_faultcode(&SmsWBS_soap), *soap_faultstring(&SmsWBS_soap));
                result = SmsWBS_soap.error;
         }

1、功能说明:

Socket connection successful: master socket = 3

        result = add(server, num1, num2, &sum);
        if (result != 0)
        {
                printf("soap err, errcode = %d n", result);
        }
        else
        {
                printf("%d + %d = %d n", num1, num2, sum);
        }

前面都已经准备好了,现在只需要:

        if (argc < 2)
        {
                printf("usage: %s <server_port> n", argv[0]);
                exit(1);
        }
        else
        {
                m = soap_bind(&SmsWBS_soap, NULL, atoi(argv[1]), 100);
                if (m < 0)
                {
                        soap_print_fault(&SmsWBS_soap, stderr);
                        exit(-1);
                }

(5)编译和运行:

stdsoap2.o:$(GSOAP_ROOT)/src/stdsoap2.c
        $(CC) -c $? $(INCLUDE)

        num1 = atoi(argv[1]);
        num2 = atoi(argv[2]);

调用Web Service成功

        soap_init(&SmsWBS_soap);
        soap_set_namespaces(&SmsWBS_soap, namespaces);

                fprintf(stderr, "Socket connection successful: master socket = %dn", m);

                        if (s < 0)
                        {
                                soap_print_fault(&SmsWBS_soap, stderr);
                                exit(-1);
                        }

(3)服务端程序SmsWBSserver.c:

server:Makefile ${WSNAME}.wsdl ${WSNAME}server.o $(SERVER_OBJS)
        $(CC) ${WSNAME}server.o $(SERVER_OBJS) -o ${WSNAME}server

        return result;
}

显示:

$(ALL_OBJS):%.o:%.c
        $(CC) -c $? $(INCLUDE)

        }

                for (;;)
                {
                        s = soap_accept(&SmsWBS_soap);

2、C版本的程序:

(4)客户端程序SmsWBStest.c:

SmsWBStest 67 78

#include <stdio.h>
#include <stdlib.h>
#include "soapStub.h"
#include "SmsWBS.nsmap"

        soap_set_namespaces(&SmsWBS_soap, namespaces);

再运行客户端程序:

        int num1 = 0;
        int num2 = 0;
        int sum = 0;

clean:
        rm -f *.o *.xml *.a *.wsdl *.nsmap $(WSNAME0)H.h $(WSNAME0)C.c $(WSNAME0)Server.c $(WSNAME0)Client.c $(WSNAME0)Stub.* $(WSNAME)$(WSNAME)Proxy.* $(WSNAME)$(WSNAME)Object.* $(WSNAME0)ServerLib.c $(WSNAME0)ClientLib.c $(WSNAME)server ns.xsd $(WSNAME)test

int ns__add(int num1, int num2, int *sum);
这个头文件需要注意的是,前面的 // 部分是有意义的,可以在上面修改,如果完全去掉,将会导致生成的中间文件不同,由此会引起要修改Makefile文件                                           

GSOAP_ROOT=/usr/local/gSOAP
WSNAME0=soap
WSNAME=SmsWBS
CC=g++ -g -DWITH_NONAMESPACES
INCLUDE=-I $(GSOAP_ROOT)/include
SERVER_OBJS=$(WSNAME0)C.o $(WSNAME0)Server.o stdsoap2.o
CLIENT_OBJS=$(GSOAP_ROOT)/env/envC.o $(WSNAME0)ClientLib.o stdsoap2.o
ALL_OBJS=${WSNAME}server.o $(WSNAME0)C.o $(WSNAME0)Server.o ${WSNAME}test.o $(WSNAME0)ClientLib.o
#GSOAP_SRC=/usr/local/gsoap-2.7/gsoap

SmsWBSserver 9000             ----运行服务端程序

${WSNAME}.wsdl:${WSNAME}.h
        $(GSOAP_ROOT)/bin/soapcpp2 -c $(GSOAP_ROOT)/import ${WSNAME}.h

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:前端高性能计算之二,C语言不定参数

关键词: