WebAssembly初探

本次分享的文章是基于WebAssembly的探索与研究。最近需要做一个与加密相关的项目,想将后端的加密方案直接放到前端使用,好处是加密方案代码只用维护一套,且后端方案更贴近系统底层,应该可以得到更好的性能。恰好发现 WebAssembly ,它是为了可移植的目标而设计的,可以满足需求。

这次研究 WebAssembly的过程中遇到了各种问题,我均记录下来,并在后期可以和大家一起分享,文末放置了参考的文章,大家可以延伸阅读。这篇文章是本系列的第一部分,主要是了解WebAssembly和WebAssembly的基本使用方法。万事开头难,我们开始吧。

概述

  • WebAssembly的诞生
  • WebAssembly是什么?
  • MAC安装Emscripten
  • WebAssembly简单使用和分析
  • 总结

一、 WebAssembly的诞生

当人们说 WebAssembly 更快的时候,一般来讲是与 JavaScript 相比而言的。

JavaScript 于 1995 年问世,它的设计初衷并不是为了执行起来快,在前 10 个年头,它的执行速度也确实不快。紧接着,浏览器市场竞争开始激烈起来。被人们广为传播的“性能大战”在 2008 年打响。许多浏览器引入了 Just-in-time 编译器,也叫 JIT。基于 JIT 的模式,JavaScript 代码的运行渐渐变快。正是由于这些 JIT 的引入,使得 JavaScript 的性能达到了一个转折点,JS 代码执行速度快了 10 倍。

随着性能的提升,JavaScript 可以应用到以前根本没有想到过的领域,比如用于后端开发的 Node.js。性能的提升使得 JavaScript 的应用范围得到很大的扩展。

但这也渐渐暴露出了 JavaScript 的问题:

  • 语法太灵活导致开发大型 Web 项目困难;
  • 性能不能满足一些场景的需要。

针对以上两点缺陷,近年来出现了一些 JS 的代替语言,例如:

  • 微软的 TypeScript 通过为 JS 加入静态类型检查来改进 JS 松散的语法,提升代码健壮性;
  • 谷歌的 Dart 则是为浏览器引入新的虚拟机去直接运行 Dart 程序以提升性能;
  • 火狐的 asm.js 则是取 JS 的子集,JS 引擎针对 asm.js 做性能优化。

以上尝试各有优缺点,其中:

  • TypeScript 只是解决了 JS 语法松散的问题,最后还是需要编译成 JS 去运行,对性能没有提升;
  • Dart 只能在 Chrome 预览版中运行,无主流浏览器支持,用 Dart 开发的人不多;
  • asm.js 语法太简单、有很大限制,开发效率低。

三大浏览器巨头分别提出了自己的解决方案,互不兼容,这违背了 Web 的宗旨; 是技术的规范统一让 Web 走到了今天,因此形成一套新的规范去解决 JS 所面临的问题迫在眉睫。

于是 WebAssembly 诞生了,WebAssembly 是一种新的字节码格式,主流浏览器都已经支持 WebAssembly。 和 JS 需要解释执行不同的是,WebAssembly 字节码和底层机器码很相似可快速装载运行,因此性能相对于 JS 解释执行大大提升。 也就是说 WebAssembly 并不是一门编程语言,而是一份字节码标准,需要用高级编程语言编译出字节码放到 WebAssembly 虚拟机中才能运行, 浏览器厂商需要做的就是根据 WebAssembly 规范实现虚拟机。

二、WebAssembly是什么?

WebAssembly(缩写 Wasm)是基于堆栈虚拟机的二进制指令格式。Wasm为了一个可移植的目标而设计的,可用于编译C/C+/RUST等高级语言,使客户端和服务器应用程序能够在Web上部署。

上面这段话是来自官方的定义。

我们可以从字面上理解,WebAssembly的名字带个汇编Assembly,所以我们从其名字上就能知道其意思是给Web使用的汇编语言,是通过Web执行低级二进制语法。但是WebAssembly并不是直接用汇编语言,而是提供了抓换机制(LLVM IR),把高级别的语言(C,C++和Rust)编译为WebAssembly,以便有机会在浏览器中运行。可以看出来它其实是一种运行机制,一种新的字节码格式(.wasm),而不是新的语言。

三、MAC安装Emscripten

如果要把一个C/C++程序编译成一个.wasm文件,是需要编译工具来完成的。WebAssembly 社区推荐常用工具:

  • Emscripten:能把 C、C++代码转换成 wasm、asm.js;

  • Binaryen:提供更简洁的 IR,把 IR 转换成 wasm,并且提供 wasm 的编译时优化、wasm 虚拟机,wasm 压缩等功能。

1. 环境依赖

  • Git
  • CMake
  • brew install cmake
  • Python 2.7.x 或者更高版本,默认安装过

2. 编译Emscripten

接下来,您需要通过源码自己编译一个Emscripten。运行下列命令来自动化地使用Emscripten SDK。

git clone https://github.com/juj/emsdk.gitcd emsdk# 编译源码
./emsdk install latest# 激活sdk
./emsdk activate latest#设置环境变量
source ./emsdk_env.sh

在运行上述命令的时候,可能会遇到如下问题:

  • ./emsdk install latest 报错:

    likai@likaideMacBook-Pro:~/resource/emsdk$ ./emsdk install latestInstalling SDK 'sdk-releases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bit'..
    Installing tool 'node-12.18.1-64bit'..
    Error: Downloading URL 'https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v12.18.1-darwin-x64.tar.gz': <urlopen error unknown url type: https>
    Warning: Possibly SSL/TLS issue. Update or install Python SSL root certificates (2048-bit or greater) supplied in Python folder or https://pypi.org/project/certifi/ and try again.
    Installation failed!
  • 解决办法:

    简单看了emsdk的内容,发现这个命令调用的是emsdk.py文件,所以使用 ./emsdk.py install latest即可解决。

    likai@likaideMacBook-Pro:~/resource/emsdk$ ./emsdk.py install latestInstalling SDK 'sdk-releases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bit'..
    Installing tool 'node-12.18.1-64bit'..
    Downloading: /Users/likai/hisun/resource/emsdk/zips/node-v12.18.1-darwin-x64.tar.gz from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v12.18.1-darwin-x64.tar.gz, 20873670 Bytes
    Unpacking '/Users/likai/hisun/resource/emsdk/zips/node-v12.18.1-darwin-x64.tar.gz' to '/Users/likai/hisun/resource/emsdk/node/12.18.1_64bit'
    Done installing tool 'node-12.18.1-64bit'.
    Installing tool 'python-3.7.4-2-64bit'..
    Downloading: /Users/likai/hisun/resource/emsdk/zips/python-3.7.4-2-macos.tar.gz from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/python-3.7.4-2-macos.tar.gz, 25365593 Bytes
    Unpacking '/Users/likai/hisun/resource/emsdk/zips/python-3.7.4-2-macos.tar.gz' to '/Users/likai/hisun/resource/emsdk/python/3.7.4-2_64bit'
    Done installing tool 'python-3.7.4-2-64bit'.
    Installing tool 'releases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bit'..
    Downloading: /Users/likai/hisun/resource/emsdk/zips/7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-wasm-binaries.tbz2 from https://storage.googleapis.com/webassembly/emscripten-releases-builds/mac/7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f/wasm-binaries.tbz2, 69799761 Bytes
    Unpacking '/Users/likai/hisun/resource/emsdk/zips/7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-wasm-binaries.tbz2' to '/Users/likai/hisun/resource/emsdk/upstream'
    Done installing tool 'releases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bit'.
    Running post-install step: npm ci ...
    Done running: npm ci
    Done installing SDK 'sdk-releases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bit'.

    同样激活 Emscripten也是使用 ./emsdk.py activate latest

    likai@likaideMacBook-Pro:~/resource/emsdk$ ./emsdk.py activate latestSetting the following tools as active:node-12.18.1-64bitpython-3.7.4-2-64bitreleases-upstream-7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f-64bitNext steps:
    - To conveniently access emsdk tools from the command line,consider adding the following directories to your PATH:/Users/likai/hisun/resource/emsdk/Users/likai/hisun/resource/emsdk/node/12.18.1_64bit/bin/Users/likai/hisun/resource/emsdk/python/3.7.4-2_64bit/bin/Users/likai/hisun/resource/emsdk/upstream/emscripten
    - This can be done for the current shell by running:source "/Users/likai/hisun/resource/emsdk/emsdk_env.sh"
    - Configure emsdk in your bash profile by running:echo 'source "/Users/likai/hisun/resource/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile

    source ./emsdk_env.sh

    likai@likaideMacBook-Pro:~/resource/emsdk$ source ./emsdk_env.shAdding directories to PATH:
    PATH += /Users/likai/hisun/resource/emsdk
    PATH += /Users/likai/hisun/resource/emsdk/upstream/emscripten
    PATH += /Users/likai/hisun/resource/emsdk/node/12.18.1_64bit/bin
    PATH += /Users/likai/hisun/resource/emsdk/python/3.7.4-2_64bit/binSetting environment variables:
    EMSDK = /Users/likai/hisun/resource/emsdk
    EM_CONFIG = /Users/likai/hisun/resource/emsdk/.emscripten
    EM_CACHE = /Users/likai/hisun/resource/emsdk/upstream/emscripten/cache
    EMSDK_NODE = /Users/likai/hisun/resource/emsdk/node/12.18.1_64bit/bin/node
    EMSDK_PYTHON = /Users/likai/hisun/resource/emsdk/python/3.7.4-2_64bit/bin/python3

3. 验证

emcc -v 不报错就成功了```
likai@likaideMacBook-Pro:~/resource/emsdk$ emcc -vemcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.3
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project a39423084cbbeb59e81002e741190dccf08b5c82)
Target: x86_64-apple-darwin19.4.0
Thread model: posix
InstalledDir: /Users/likai/hisun/resource/emsdk/upstream/bin
shared:INFO: (Emscripten: Running sanity checks)```获取帮助 emcc --help,内容过多就不展示了。看下emcc 的版本是2.0.3```
likai@likaideMacBook-Pro:~/resource/emsdk$  emcc --versionemcc (Emscripten gcc/clang-like replacement) 2.0.3 (43fcfd2938b72c57373a910ece897b27aa298852)
Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.```

四、WebAssembly简单使用和分析

到这里WebAssembly的编译工具已经安装好了,我们使用两个官方样例,看一下WebAssembly是如何使用的,方便后面的学习。

当使用Emscripten来编译的时候有很多种不同的选择,我们介绍其中主要的2种:

  • 编译到 wasm 并且生成一个用来运行我们代码的HTML,将所有 wasm 在web环境下运行所需要的 “胶水” JavaScript代码都添加进去。

  • 编译到 wasm,使用JavaScript调用wasm里边的方法。。

1. 生成 HTML 和 JavaScript

  • 找个目录创建hello_world.c文件

    #include <stdio.h>int main(int argc, char ** argv) {printf("Hello World\n");
    }
  • 使用刚才已经配置过的终端,找到hello_world.c文件,执行如下命令

    emcc ./hello_world.c -s WASM=1 -o ./hello_world.html
    • emcc 是Emscripten编译器行命令

    • hello_world.c 是我们的输入文件

    • -s WASM=1 指定我们想要的wasm输出形式。如果我们不指定这个选项,Emscripten默认将只会生成asm.js。(可参考 emcc --help 参数说明)

    • -o ./hello_world.html 指定这个选项将会生成HTML页面来运行我们的代码,并且会生成wasm模块,以及编译和实例化wasm模块所需要的“胶水”js代码,这样我们就可以直接在web环境中使用了。

    likai@likaideMacBook-Pro:~/resource/emsdk/demo$ emcc ./hello_world.c -s WASM=1 -o ./hello_world.html
    shared:INFO: (Emscripten: Running sanity checks)likai@likaideMacBook-Pro:~/resource/emsdk/demo$ ls
    hello_world.c    hello_world.html hello_world.js   hello_world.wasm
    

    执行后会产生三个新文件:

    • hello_world.wasm 二进制的wasm模块代码,虽然本地打不开,但是浏览器可以帮忙翻译。
    • hello_world.js 一个包含了用来在原生C函数和JavaScript/wasm之间转换的胶水代码的JavaScript文件
    • hello_world.html 一个用来加载,编译,实例化你的wasm代码并且将它输出在浏览器显示上的一个HTML文件
  • 启动http服务命令,查看运行结果

    emrun --no_browser --port 8080 ./hello_world.html

    likai@likaideMacBook-Pro:~/resource/emsdk/demo$ emrun --no_browser --port 8080 ./hello_world.htmlWeb server root directory: /Users/likai/hisun/resource/emsdk/demo
    Now listening at http://0.0.0.0:8080/
    • emrun 这个命令也是emsdk中自带的直接使用即可。

    可以看到原来helloworld.c文件中打印的内容现在了浏览器中。我很好奇C代码中的打印结果是怎么跑到浏览器的控制台上的。看似很简单的操作实际上Emscripten做了很多事,点开生成胶水代码hello_world.js看了下,里面写了很多代码2000多行嘞,加载wasm,处理内存分配、内存释放、垃圾回收、函数调用,封装了各种方法。编译后的js文件我放在了gihub中,点击查看 hello_world.js
    简单分析一下胶水代码的内容,有助于我们对WebAssembly的理解,对于后面的使用会很有帮助。

    先一起看下.wasm的真容,上面提到了.wasm是个二进制文件,打不开,想要看里面内容的话推荐反编译工具wasm2wast,当然浏览器也可以解析,我们通过浏览器简单看下。 右键打开控制台–>Sources–>hello_world.wasm

    果然这个文件看得不太懂,看到了module,我猜这大概是个模块,我找到了main函数,不知道是不是hello_world.c的main,我们还是看胶水代码吧。

    从胶水代码hello_world.js中可以看到,载入了WebAssembly汇编模块(.wasm),原来这个.wasm被胶水代码加载了一下,核心部分如下:

    function instantiateArrayBuffer(receiver) {
    return getBinaryPromise().then(function(binary) {return WebAssembly.instantiate(binary, info);
    }).then(receiver, function(reason) {err('failed to asynchronously prepare wasm: ' + reason);abort(reason);
    });
    }// Prefer streaming instantiation if available.
    function instantiateAsync() {
    if (!wasmBinary &&typeof WebAssembly.instantiateStreaming === 'function' &&!isDataURI(wasmBinaryFile) &&// Don't use streaming for file:// delivered objects in a webview, fetch them synchronously.!isFileURI(wasmBinaryFile) &&typeof fetch === 'function') {fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {var result = WebAssembly.instantiateStreaming(response, info);return result.then(receiveInstantiatedSource, function(reason) {// We expect the most common failure cause to be a bad MIME type for the binary,// in which case falling back to ArrayBuffer instantiation should work.err('wasm streaming compile failed: ' + reason);err('falling back to ArrayBuffer instantiation');return instantiateArrayBuffer(receiveInstantiatedSource);});});
    } else {return instantiateArrayBuffer(receiveInstantiatedSource);
    }
    }
    

    主要做了如下几件事情:

    • 尝试使用WebAssembly.instantiateStreaming()方法创建wasm模块的实例;

    • 如果流式创建失败,则改用WebAssembly.instantiate()方法创建实例;

    • 成功实例化后的返回值交由receiveInstantiatedSource()方法处理。

      receiveInstantiatedSource()代码

      function receiveInstance(instance, module) {var exports = instance.exports;Module['asm'] = exports;removeRunDependency('wasm-instantiate');
      }......function receiveInstantiatedSource(output) {// 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance.// receiveInstance() will swap in the exports (to Module.asm) so they can be calledassert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?');trueModule = null;// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.// When the regression is fixed, can restore the above USE_PTHREADS-enabled path.receiveInstance(output['instance']);
      }

      receiveInstantiatedSource()方法调用了receiveInstance()方法,后者的这条指令:

      Module['asm'] = exports;
      

      将wasm模块实例的导出对象传给了Module的子对象asm。倘若我们在上述函数中手动添加打印实例导出对象的代码。

      function receiveInstance(instance, module) {
      ... ...
      Module['asm'] = exports;
      console.log(Module['asm']);  //print instance.exports
      ... ...

      由此可见,上述一系列代码运行后,Module[‘asm’]中保存了WebAssembly实例的导出对象——而导出函数恰是WebAssembly实例供外部调用最主要的入口。

      看看我理解的对不,wasm的编译器把C代码编译了.wasm文件,这个文件是个汇编代码,里面有C代码的内容,胶水代码去加载.wasm文件,通过WebAssembly实例对外提供了C代码里面的方法,然后使用javascript调用C代码。最后给人的感觉就是浏览器上能运行C语言的程序。

      我们再一起细品下官方原话(翻译过的):

      WebAssembly(缩写 Wasm)是基于堆栈虚拟机的二进制指令格式。Wasm为了一个可移植的目标而设计的,可用于编译C/C+/RUST等高级语言,使客户端和服务器应用程序能够在Web上部署。
      
      • Wasm是基于堆栈虚拟机的二进制指令格式,hello_world.wasm本地打开是个二进制指令格式。
      • 可用于编译C/C+/RUST等高级语言,使用Emscripten编译hello_world.c文件。
      • 使客户端和服务器应用程序能够在Web上部署。 确实在浏览器上跑起来了。
      • Wasm为了一个可移植的目标而设计的。要是这么说的话,我岂不是可以把加密工具,编译成wasm,然后通过胶水代码来调用了么,下一篇我们一起搞一下。

2. 编译到 wasm,使用JavaScript调用wasm里边的方法。

这个很好理解,就是在编译的时候,不生成默认推荐的html,只生成wasm,然后直接调用wasm即可。这就要我们自己写胶水代码,下面看个简单的例子。步骤如下:

  1. 写一个test.c文件,里面是加减乘除计算。
  2. 编译成.wasm文件
  3. 写一个html,调用.wasm文件
  • test.c文件
char* toChar (char* str) {return str;}int add (int x, int y) {return x + y;}int square (int x) {return x * x;}
  • 编译成.wasm文件

    emcc ./test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o ./test.wasm

    这个命令好像和上面不一样,解释下:

    • emcc就是Emscripten编译器,
    • test.c是我们的输入文件
    • Os表示这次编译需要优化(可以指定优化策略。emcc --help)
    • -s WASM=1表示输出wasm的文件,因为默认的是输出asm.js
    • -s SIDE_MODULE=1表示就只要这一个模块,不要给我其他乱七八糟的代码
    • -o test.wasm是我们的输出文件。
  • 写一个html,调用.wasm文件。test.html 这两个函数是关键:

    function loadWebAssembly (path, imports = {}) {return fetch(path) // 加载文件.then(response => response.arrayBuffer()) // 转成 ArrayBuffer.then(buffer => WebAssembly.compile(buffer)).then(module => {imports.env = imports.env || {}// 开辟内存空间imports.env.memoryBase = imports.env.memoryBase || 0if (!imports.env.memory) {imports.env.memory = new WebAssembly.Memory({ initial: 256 })}// 创建变量映射表imports.env.tableBase = imports.env.tableBase || 0if (!imports.env.table) {// 在 MVP 版本中 element 只能是 "anyfunc"imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' })}// 创建 WebAssembly 实例return new WebAssembly.Instance(module, imports)})}  // 加载wasm文件
    loadWebAssembly('test.wasm').then(instance => {//调用c里面的方法const toChar = instance.exports.toCharconst add = instance.exports.addconst square = instance.exports.squareconsole.log('return:   ', toChar("12352324"))console.log('10 + 20 =', add(10, 20))console.log('3*3 =', square(3))console.log('(2 + 5)*2 =', square(add(2 + 5)))})

    有了第一个案例的理解,就大概知道这个意思了,创建了一个WebAssembly的实例,返回WebAssembly导出对象,调用了test.c里面的函数。这里面有一些胶水代码语法相关的知识。MDN Web docs-WebAssembly

  • 运行结果

  • test.wasm

可以看到优化后的wasm文件,只有这几个函数了,并且可以看出包含导出test.c中的函数。

五、总结

我们今天通过两个简单的例子讲述了WebAssembly的使用,也进一步理解了WebAssembly是什么,整体的流程是这样的:

使用Emscripten编译C语言源代码,生成.wasm文件和胶水代码,通过javascript调用胶水代码或者.wasm,使C语言的程序在浏览器中运行。

以上就是这篇文章要分享的全部内容了,下一篇,基于wasm的加密工具。

文章参考

Webassembly官方网站

MDN Web docs-WebAssembly

中文原文

基于wasm的探索与研究(一)相关推荐

  1. 基于wasm的探索与研究(三)

    wasmer运行时 上一篇文章分享了基于wasm的openssl实践,讲述了openssl的MD5算法如何在浏览器中执行.在探索过程中发现了openssl是可以通过wasm编译后直接run,并且有自己 ...

  2. 基于wasm的探索与研究(二)

    基于wasm的openssl实践 上一篇文章分享了WebAssembly概念和基本使用,通过两个代码示例的分析对WebAssembly有了大致的了解.这一篇文章分享的是基于WebAssembly的加密 ...

  3. 基于wasm的探索与研究(四)

    wasm runtime 性能测试 上一篇文章分享了wasmer runtime,编译好的openssl.wasm文件可以独立运行,这篇文章分享openssl.wasm和原生openssl之间的性能对 ...

  4. 会计专业应用计算机操作,基于中职会计专业《计算机应用基础》课程的教学探索与研究.doc...

    基于中职会计专业<计算机应用基础>课程的教学探索与研究 基于中职会计专业<计算机应用基础>课程的教学探索与研究 谭仔群 桂林市财贸管理干部中等专业学校 X 关注成功! 加关注后 ...

  5. 基于图神经网络的聚类研究与应用

    Datawhale干货 本文编辑:Datawhale 用手机上网的时候,总有种感觉,推荐的视频是我爱看的,推荐的美食是我爱吃的,大家长的又好看,说话又好听. 有时候会对自己发出灵魂拷问:难道隐私被记录 ...

  6. 人工智能的影响调查_调查报告|文科大学生群体对于人工智能影响 就业的认知程度:基于访谈的质性研究...

    摘 要:人工智能技术的迅速发展给各个行业带来的不同程度的替代效应和收入效应已经受到关注.本文基于人工智能在短期到中期将冲击文科类专业就业结构的背景,以文科类大学生作为目标群体,通过12次基于访谈的质性 ...

  7. 基于WASM的H265 Web播放器

    基于WASM的H265 Web播放器 1 背景 2 代码 3 依赖 3.1 WASM 3.2 FFmpeg 3.3 WebGL 3.4 Web Audio 4 播放器实现 4.1 模块结构 4.2 线 ...

  8. 用matlab画声波,基于MATLAB的声波分析研究-复旦大学物理教学试验中心.PDF

    基于MATLAB的声波分析研究-复旦大学物理教学试验中心 第 27 卷 第 7 期 实 验 室 研 究 与 探 索 Vol. 27 No. 7 2008年 7 月 RESEARCH AND EXPLO ...

  9. 基于深度学习的NLP研究大盘点

    AI深入浅出 公众号ID:xiumius 关注 在过去的几年里,深度学习(DL)架构和算法在诸如图像识别和语音处理等领域取得了世人瞩目的进步.然而在最开始的时候,深度学习在自然语言处理(Natural ...

最新文章

  1. Windows中的tree命令不可用的解决办法
  2. 旷视5号员工陈可卿:1991生于绍兴、10岁买电脑改变命运,信息奥赛金牌保送清华...
  3. 2021-08-25路演的反馈
  4. python 函数前有一个下划线_【Python】怎么写好一个 Python 函数?
  5. EOJ_1094_寻找航海路线
  6. 深入javascript中的exec与match方法
  7. QGS/300 performance issue
  8. grub4dos中的不容易理解的问题
  9. EbN0、SNR、0.1nmOSNR的区别与联系
  10. 673. 最长递增子序列的个数
  11. sdut 区间覆盖问题
  12. 向上传文件服务器,向服务器上传文件
  13. linux添加sshkey,使用SSH密钥对连接Linux实例
  14. 全国青少年软件编程(C语言)等级考试试题-2019年9月(一级含答案)
  15. python调用Go代码
  16. 实现putchar put_str put_int
  17. C++经典程序代码大全
  18. 计算机创新论文特点,计算机应用技术的创新分析
  19. QQ邮箱收不到GitHub验证邮件
  20. Chapter 2 (Matrix Algebra): Partitioned matrices (分块矩阵)

热门文章

  1. 如何使用Fiddler模拟弱网情况对app进行测试
  2. flutter 点击旋转动画_让动画实现更简单,Flutter 动画简易教程!
  3. (一)psql的使用
  4. VBA设置默认/缺省运行路径的方法
  5. python两个excel字段模糊匹配_Python对两个Excel操作
  6. (转载) 到底什么样的杀毒软件好
  7. vue 动态改变主题颜色
  8. java 时间戳 timestamp
  9. android 画 月牙曲线,牙齿矫正真能改变脸型?这四类人最有发言权
  10. linux修改sftp端口的方法:2个地方需要修改