精华内容
下载资源
问答
  • 二进制码与格雷码PK

    2020-07-17 21:04:02
    大家在写程序的时候,可能会听闻,什么独热码,什么格雷码,什么二进制码等等,本节意在解释这几种编码之间的区别和优势以及用verilog怎么去实现,下面先介绍这几种编码的区别。
  • 为了改进算法的执行效率,提出BE-Apriori(binary encoded Apriori)算法,其充分利用了二进制数相比编程语言中各种数据结构在内存及运算速度上的优势,对事务记录进行二进制编码后加载到内存,然后利用等效的二进制...
  • 众所周知,go语言可打包成目标平台二进制文件是其一大优势,如此go项目在服务器不需要配置go环境和依赖就可跑起来。 操作 需求:打包部署到centos7 笔者打包环境:mac os 方法:进入main.go所在目录,输入如下命令,...
  • 键入二进制JSON 类型化二进制JSON或TBJSON是与JSON兼容的二进制序列化格式。 它将已知的对象原型存储在JSON标头中,并在标头之后以二进制格式序列化数据。 TBJSON对于序列化已知对象,类或类型很有用,否则就大小...
  • 仿真结果表明,与基本的二进制树型搜索算法和动态二进制树型搜索算法相比,该算法在传输二进制数据的长度方面有明显的优势,并且在平均请求数量方面,它提供了相同的性能,从而使识别过程更快,减少了信息的传播时间.
  • 二进制引入关联规则求解中,充分利用二进制操作方便、运算速度快、节省空间的优势。在求解事务项集真子集和支持度时,对事务数据库中相同事务只求解一次,并给出了真子集的具体求解算法。本算法一次扫描数据库可以...
  • 无论是与传统汇编语言子程序,还是与使用sprintf()函数的程序相比,快速算法都有很大的速度优势;是一种针对8位机的创新算法,具有很强的工程实用性,值得推广应用。
  • 1 认识二进制数据 二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”,由18世纪德国数理哲学大师莱布尼兹发现。—— ...

    1 认识二进制数据

    二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”,由18世纪德国数理哲学大师莱布尼兹发现。

    —— 百度百科

    二进制数据就像上图一样,由0和1来存储数据。普通的十进制数转化成二进制数一般采用"除2取余,逆序排列"法,用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为小于1时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。例如,数字10转成二进制就是1010,那么数字10在计算机中就以1010的形式存储。

    而字母和一些符号则需要通过 ASCII 码来对应,例如,字母a对应的 ACSII 码是 97,二进制表示就是0110 0001。JavaScript 中可以使用 charCodeAt 方法获取字符对应的 ASCII:

    b69b4bd10cb6e46634f242d6a54dca0a.png

    除了ASCII外,还有一些其他的编码方式来映射不同字符,比如我们使用的汉字,通过 JavaScript 的 charCodeAt 方法得到的是其 UTF-16 的编码。

    a81f52d45a114d3270421c6c9ba3d40f.png

    Node 处理二进制数据

    JavaScript 在诞生初期主要用于表单信息的处理,所以 JavaScript 天生擅长对字符串进行处理,可以看到 String 的原型提供特别多便利的字符串操作方式。

    b0b17270f269bcb4df1f4b3ca59f14d9.png

    但是,在服务端如果只能操作字符是远远不够的,特别是网络和文件的一些 IO 操作上,还需要支持二进制数据流的操作,而 Node.js 的 Buffer 就是为了支持这些而存在的。好在 ES6 发布后,引入了类型数组(TypedArray)的概念,又逐步补充了二进制数据处理的能力,现在在 Node.js 中也可以直接使用,但是在 Node.js 中,还是 Buffer 更加适合二进制数据的处理,而且拥有更优的性能,当然 Buffer 也可以直接看做 TypedArray 中的 Uint8Array。除了 Buffer,Node.js 中还提供了 stream 接口,主要用于处理大文件的 IO 操作,相对于将文件分批分片进行处理。

    2 认识 Buffer

    Buffer 直译成中文是『缓冲区』的意思,顾名思义,在 Node.js 中实例化的 Buffer 也是专门用来存放二进制数据的缓冲区。一个 Buffer 可以理解成开辟的一块内存区域,Buffer 的大小就是开辟的内存区域的大小。下面来看看Buffer 的基本使用方法。

    API 简介

    早期的 Buffer 通过构造函数进行创建,通过不同的参数分配不同的 Buffer。

    new Buffer(size)

    创建大小为 size(number) 的 Buffer。

    new Buffer(5)// 

    new Buffer(array)

    使用八位字节数组 array 分配一个新的 Buffer。

    const buf = new Buffer([0x74, 0x65, 0x73, 0x74])// // 对应 ASCII 码,这几个16进制数分别对应 t e s t// 将 Buffer 实例转为字符串得到如下结果buf.toString() // 'test'

    new Buffer(buffer)

    拷贝 buffer 的数据到新建的 Buffer 实例。

    const buf1 = new Buffer('test')const buf2 = new Buffer(buf1)

    new Buffer(string[, encoding])

    创建内容为 string 的 Buffer,指定编码方式为 encoding。

    const buf = new Buffer('test')// // 可以看到结果与 new Buffer([0x74, 0x65, 0x73, 0x74]) 一致buf.toString() // 'test'

    更安全的 Buffer

    由于 Buffer 实例因第一个参数类型而执行不同的结果,如果开发者不对参数进行校验,很容易导致一些安全问题。例如,我要创建一个内容为字符串 "20" 的 Buffer,而错误的传入了数字 20,结果创建了一个长度为 20 的Buffer 实例。

    e3d1dfc9ef4c3ae28e0a974d6a0f6dec.png

    可以看到上图,Node.js 8 之前,为了高性能的考虑,Buffer 开辟的内存空间并未释放之前已存在的数据,直接将这个 Buffer 返回可能导致敏感信息的泄露。因此,Buffer 类在 Node.js 8 前后有一次大调整,不再推荐使用 Buffer 构造函数实例 Buffer,而是改用Buffer.from()、Buffer.alloc() 与 Buffer.allocUnsafe() 来替代 new Buffer()。

    Buffer.from()

    该方法用于替代 new Buffer(string)、new Buffer(array)、new Buffer(buffer)。

    Buffer.alloc(size[, fill[, encoding]])

    该方法用于替代 new Buffer(size),其创建的 Buffer 实例默认会使用 0 填充内存,也就是会将内存之前的数据全部覆盖掉,比之前的 new Buffer(size) 更加安全,因为要覆盖之前的内存空间,也意味着更低的性能。

    同时,size 参数如果不是一个数字,会抛出 TypeError。

    0f4f616f08eee9206421aafa293e2717.png

    Buffer.allocUnsafe(size)

    该方法与之前的 new Buffer(size) 保持一致,虽然该方法不安全,但是相比起 alloc 具有明显的性能优势。

    43aa486fc3db0dd0a1a69d48faa975ee.png

    Buffer 的编码

    前面介绍过二进制数据与字符对应需要指定编码,同理将字符串转化为 Buffer、Buffer 转化为字符串都是需要指定编码的。

    Node.js 目前支持的编码方式如下:

    • hex:将每个字节编码成两个十六进制的字符。

    • ascii:仅适用于 7 位 ASCII 数据。此编码速度很快,如果设置则会剥离高位。

    • utf8:多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8。

    • utf16le:2 或 4 个字节,小端序编码的 Unicode 字符。

    • ucs2:utf16le 的别名。

    • base64:Base64 编码。

    • latin1:一种将 Buffer 编码成单字节编码字符串的方法。

    • binary:latin1 的别名。

    比较常用的就是 UTF-8、UTF-16、ASCII,前面说过 JavaScript 的 charCodeAt 使用的是 UTF-16 编码方式,或者说 JavaScript 中的字符串都是通过 UTF-16 存储的,不过 Buffer 默认的编码是 UTF-8。

    49376e2abc04a74ef0cfe62a39629677.png

    可以看到一个汉字在 UTF-8 下需要占用 3 个字节,而 UTF-16 只需要 2 个字节。主要原因是 UTF-8 是一种可变长的字符编码,大部分字符使用 1 个字节表示更加节省空间,而某些超出一个字节的字符,则需要用到 2 个或 3 个字节表示,大部分汉字在 UTF-8 中都需要用到 3 个字节来表示。UTF-16 则全部使用 2 个字节来表示,对于一下超出了 2 字节的字符,需要用到 4 个字节表示。2 个字节表示的 UTF-16 编码与 Unicode 完全一致,通过汉字Unicode编码表可以找到大部分中文所对应的 Unicode 编码。前面提到的 『汉』,通过 Unicode 表示为 6C49。

    1d9d868f3a7c7b50cf4ab7cd5653efdf.png

    这里提到的 Unicode 编码又被称为统一码、万国码、单一码,它为每种语言都设定了统一且唯一的二进制编码,而上面说的 UTF-8、UTF-16 都是他的一种实现方式。更多关于编码的细节不再赘述,也不是本文的重点,如果想了解更多可自行搜索。

    乱码的原因

    我们经常会出现一些乱码的情况,就是因为在字符串与 Buffer 的转化过程中,使用了不同编码导致的。

    我们先新建一个文本文件,然后通过 utf16 编码保存,然后通过 Node.js 读取改文件。

    003113c07339d5b3d81af324be074276.png

    const fs = require('fs')const buffer = fs.readFileSync('./1.txt')console.log(buffer.toString())

    0e7483400b2c6ae15030423ec7134134.png

    由于 Buffer 在调用 toString 方法时,默认使用的是 utf8 编码,所以输出了乱码,这里我们将 toString 的编码方式改成 utf16 就可以正常输出了。

    const fs = require('fs')const buffer = fs.readFileSync('./1.txt')console.log(buffer.toString('utf16le'))

    4395c20989ef5e00ca8ddf86debcb1e6.png

    3 认识 Stream

    前面我们说过,在 Node.js 中可以利用 Buffer 来存放一段二进制数据,但是如果这个数据量非常的大使用 Buffer 就会消耗相当大的内存,这个时候就需要用到 Node.js 中的 Stream(流)。要理解流,就必须知道管道的概念。

    在类Unix操作系统(以及一些其他借用了这个设计的操作系统,如Windows)中,管道是一系列将标准输入输出链接起来的进程,其中每一个进程的输出被直接作为下一个进程的输入。这个概念是由道格拉斯·麦克罗伊为Unix 命令行发明的,因与物理上的管道相似而得名。

    -- 摘自维基百科

    我们经常在 Linux 命令行使用管道,将一个命令的结果传输给另一个命令,例如,用来搜索文件。

    ls | grep code

    这里使用 ls 列出当前目录的文件,然后交由 grep 查找包含 code 关键词的文件。

    在前端的构建工具 gulp 中也用到了管道的概念,因为使用了管道的方式来进行构建,大大简化了工作流,用户量一下子就超越了 grunt。

    // 使用 gulp 编译 scssconst gulp = require('gulp')const sass = require('gulp-sass')const csso = require('gulp-csso')gulp.task('sass', function () {  return gulp.src('./**/*.scss')    .pipe(sass()) // scss 转 css    .pipe(csso()) // 压缩 css    .pipe(gulp.dest('./css'))})

    前面说了这么多管道,那管道和流直接应该怎么联系呢。流可以理解为水流,水要流向哪里,就是由管道来决定的,如果没有管道,水也就不能形成水流了,所以流必须要依附管道。在 Node.js 中所有的 IO 操作都可以通过流来完成,因为 IO 操作的本质就是从一个地方流向另一个地方。例如,一次网络请求,就是将服务端的数据流向客户端。

    const fs = require('fs')const http = require('http')const server = http.createServer((request, response) => {    // 创建数据流    const stream = fs.createReadStream('./data.json')    // 将数据流通过管道传输给响应流    stream.pipe(response)})server.listen(8100)// data.json{ "name": "data" }

    d02d186fc74f0ab5e741ab5c7611bd82.png

    使用 Stream 会一边读取 data.json 一边将数据写入响应流,而不是像 Buffer 一样,先将整个 data.json 读取到内存,然后一次性输出到响应中,所以使用 Stream 的时候会更加节约内存。

    其实 Stream 在内部依然是运作在 Buffer 上。如果我们把一段二进制数据比做一桶水,那么通过 Buffer 进行文件传输就是直接将一桶水倒入到另一个桶里面,而使用 Stream,就是将桶里面的水通过管道一点点的抽取过去。

    Stream 与 Buffer 内存消耗对比

    这里如果只是口头说说可能感知不明显,现在分别通过 Stream 和 Buffer 来复制一个 2G 大小的文件,看看 node 进程的内存消耗。

    Stream 复制文件

    // Stream 复制文件const fs = require('fs');const file = './file.mp4';fs.createReadStream(file)  .pipe(fs.createWriteStream('./file.copy.mp4'))  .on('finish', () => {      console.log('file successfully copy');  })

    dc24f5b2b52bd1619e6f0735bae466bd.png

    Buffer 复制文件

    // Buffer 复制文件const fs = require('fs');const file = './file.mp4';// fs.readFile 直接输出的是文件 Bufferfs.readFile(file, (err, buffer) => {    fs.writeFile('./file.copy.mp4', buffer, (err) => {        console.log('file successfully copy');    });});

    ed3a37136d68605dd98fb206f40798bc.png

    通过上图的结果可以看出,通过 Stream 拷贝时,只占用了我电脑 0.6% 的内存,而使用 Buffer 时,占用了 15.3% 的内存。

    API 简介

    在 Node.js 中,Steam 一共被分为五种类型。

    可读流(Readable),可读取数据的流;

    可写流(Writable),可写入数据的流;

    双工流(Duplex),可读又可写的流;

    转化流(Transform),在读写过程中可任意修改和转换数据的流(也是可读写的流);

    所有的流都可以通过 .pipe 也就是管道(类似于 linux 中的 |)来进行数据的消费。另外,也可以通过事件来监听数据的流动。不管是文件的读写,还是 http 的请求、响应都会在内部自动创建 Stream,读取文件时,会创建一个可读流,输出文件时,会创建可写流。

    可读流(Readable)

    虽然叫做可读流,但是可读流也是可写的,只是这个写操作一般是在内部进行的,外部只需要读取就行了。

    可读流一般分为两种模式:

    流动模式:表示正在读取数据,一般通过事件监听来获取流中的数据。

    暂停模式:此时流中的数据不会被消耗,如果在暂停模式需要读取可读流的数据,需要显式调用stram.read()。

    可读流在创建时,默认为暂停模式,一旦调用了 .pipe,或者监听了 data 事件,就会自动切换到流动模式。

    const { Readable } = require('stream')// 创建可读流const readable = new Readable()// 绑定 data 事件,将模式变为流动模式readable.on('data', chunk => {  console.log('chunk:', chunk.toString()) // 输出 chunk})// 写入 5 个字母for (let i = 97; i < 102; i++) {  const str = String.fromCharCode(i);  readable.push(str)}// 推入 `null` 表示流已经结束readable.push(null)

    3e510387857e218e538829ea4c7daf2c.png

    const { Readable } = require('stream')// 创建可读流const readable = new Readable()// 写入 5 个字母for (let i = 97; i < 102; i++) {  const str = String.fromCharCode(i);  readable.push(str)}// 推入 `null` 表示流已经结束readable.push('\n')readable.push(null)// 通过管道将流的数据输出到控制台readable.pipe(process.stdout)

    56b0d66b33463986804f9be09f8fa6b0.png

    上面的代码都是手动创建可读流,然后通过 push 方法往流里面写数据的。前面说过,Node.js 中数据的写入都是内部实现的,下面通过读取文件的 fs 创建的可读流来举例:

    const fs = require('fs')// 创建 data.json 文件的可读流const read = fs.createReadStream('./data.json')// 监听 data 事件,此时变成流动模式read.on('data', json => {  console.log('json:', json.toString())})

    01b1f1d159bcd48087cc4960ae9ce03f.png

    可写流(Writable)

    可写流对比起可读流,它是真的只能写,属于只进不出的类型,类似于貔貅。

    创建可写流的时候,必须手动实现一个 _write() 方法,因为前面有下划线前缀表明这是内部方法,一般不由用户直接实现,所以该方法都是在 Node.js 内部定义,例如,文件可写流会在该方法中将传入的 Buffer 写入到指定文本中。

    写入如果结束,一般需要调用可写流的 .end() 方法,表示结束本次写入,此时还会调用 finish 事件。

    const { Writable } = require('stream')// 创建可写流const writable = new Writable()// 绑定 _write 方法,在控制台输出写入的数据writable._write = function (chunk) {  console.log(chunk.toString())}// 写入数据writable.write('abc')// 结束写入writable.end()_write 方法也可以在实例可写流的时候,通过传入对象的 write 属性来实现。const { Writable } = require('stream')// 创建可写流const writable = new Writable({  // 同,绑定 _write 方法    write(chunk) {    console.log(chunk.toString())  }})// 写入数据writable.write('abc')// 结束写入writable.end()

    69fe3b3398c63bfcca094c5193840e17.png

    下面看看 Node.js 中内部通过 fs 创建的可写流。

    const fs = require('fs')// 创建可写流const writable = fs.createWriteStream('./data.json')// 写入数据,与自己手动创建的可写流一致writable.write(`{  "name": "data"}`)// 结束写入writable.end()

    看到这里就能理解,Node.js 在 http 响应时,需要调用 .end() 方法来结束响应,其实内部就是一个可写流。现在再回看前面通过 Stream 来复制文件的代码就更加容易理解了。

    const fs = require('fs');const file = './file.mp4';fs.createReadStream(file)  .pipe(fs.createWriteStream('./file.copy.mp4'))  .on('finish', () => {      console.log('file successfully copy');  })

    双工流(Duplex)

    双工流同时实现了 Readable 和 Writable,具体用法可以参照可读流和可写流,这里就不占用文章篇幅了。

    管道串联

    前面介绍了通过管道(.pipe())可以将一个桶里的数据转移到另一个桶里,但是有多个桶的时候,我们就需要多次调用 .pipe()。例如,我们有一个文件,需要经过 gzip 压缩后重新输出。

    const fs = require('fs')const zlib = require('zlib')const gzip = zlib.createGzip() // gzip 为一个双工流,可读可写const input = fs.createReadStream('./data.json')const output = fs.createWriteStream('./data.json.gz')input.pipe(gzip) // 文件压缩gzip.pipe(output) // 压缩后输出

    面对这种情况,Node.js 提供了 pipeline() api,可以一次性完成多个管道操作,而且还支持错误处理。

    const { pipeline } = require('stream')const fs = require('fs')const zlib = require('zlib')const gzip = zlib.createGzip()const input = fs.createReadStream('./data.json')const output = fs.createWriteStream('./data.json.gz')pipeline(  input,   // 输入  gzip,    // 压缩  output,  // 输出  // 最后一个参数为回调函数,用于错误捕获  (err) => {    if (err) {      console.error('压缩失败', err)    } else {      console.log('压缩成功')    }  })

    4 参考

    字符编码笔记

    Buffer | Node.js API

    stream | Node.js API

    stream-handbook

    43ad47aced688f3ddfd3149efd8b736b.png

    关注我们

    85566ce64ba7c190cbf17a14334ccfca.png 4f372fb3e069a12b2ccb4dc12b97c67a.png

    公众号ID:前端Sharing

    冰淇淋里有夏天的味道

    展开全文
  • 为了克服普通二进制算法交互次数多、通信数据量大的缺点,提出了一种改进二进制防碰撞算法。新算法首先进行信息预处理,识别过程中只处理冲突位,并以待识别标签为叶子节点反向构建识别树,在叶子节点的父层实现标签...
  • 二进制的概念及运算

    2021-10-30 17:19:24
    有的朋友觉得写代码做开发应该就是专注于开发出功能,管这些二进制干嘛呢,尤其是做上层开发的朋友,但是当自己出去面试的时候就有可能会碰壁,或者是在看源码的时候就会懵,打个比方我们在看hashmap的源码的时候,...

    前言

    有的朋友觉得写代码做开发应该就是专注于开发出功能,管这些二进制干嘛呢,尤其是做上层开发的朋友,但是当自己出去面试的时候就有可能会碰壁,或者是在看源码的时候就会懵,打个比方我们在看hashmap的源码的时候,并不是每个人都能马上算出这些对应的数据的,这些都是要牵涉二进制的运算的:

     

    二进制的基本概念

    进制(binary),发现者莱布尼茨,是在数学数字电路中以2为基数的记数系统,是以2为基数代表系统的二进位制。这一系统中,通常用两个不同的符号0(代表零)和1(代表一)来表示。数字电子电路中,逻辑门的实现直接应用了二进制,现代的计算机和依赖计算机的设备里都使用二进制。每个数字称为一个比特(Bit,Binary digit的缩写)。

    无论我们上层开发是用的什么语言,到了底层都是以二进制的形式存储的,所以不管我们是学什么编程语言的都需要懂得二进制的运算,这是一个合格的开发人员的基本功,

    二进制的优势

    说到优势当然是跟十进制比较,因为我们生活中用得最多的就是10进制,而计算机底层为什么要采用二进制存储而不用10进制,具体的优势如下:

    1、电路中容易实现 :当计算机工作的时候,电路通电工作,于是每个输出端就有了电压。电压的高低通过模数转换即转换成了二进制:高电平是由1表示,低电平由0表示。也就是说将模拟电路转换成为数字电路。这里的高电平与低电平可以人为确定,一般地,2.5伏以下即为低电平,3.2伏以上为高电平。二进制数码只有两个(“0”和“1”)。电路只要能识别低、高就可以表示“0”和“1”。

    2、物理上最易实现存储 :

    (1)基本道理:二进制在物理上最易实现存储,通过磁极的取向、表面的凹凸、光照的有无等来记录。

    (2)具体道理:对于只写一次的光盘,将激光束聚住成1--2um的小光束,依靠热的作用融化盘片表面上的碲合金薄膜,在薄膜上形成小洞(凹坑),记录下“1”,原来的位置表示记录“0”。

    3、便于进行加、减运算和计数编码。易于进行转换,二进制与十进制数易于互相转换。

    简化运算规则:两个二进制数和、积运算组合各有三种,运算规则简单,有利于简化计算机内部结构,提高运算速度。电子计算机能以极高速度进行信息处理和加工,包括数据处理和加工,而且有极大的信息存储能力。数据在计算机中以器件的物理状态表示,采用二进制数字系统,计算机处理所有的字符或符号也要用二进制编码来表示。用二进制的优点是容易表示, 运算规则简单,节省设备。人们知道,具有两种稳定状态的元件(如晶体管的导通和截止,继电器的接通和断开,电脉冲电平的高低等)容易找到,而要找到具有10种稳定状态的元件来对应十进制的10个数就困难了

    4、便于逻辑判断(是或非)。适合逻辑运算:逻辑代数是逻辑运算的理论依据,二进制只有两个数码,正好与逻辑代数中的“真”和“假”相吻合。二进制的两个数码正好与逻辑命 题中的“真(Ture)”、“假(False)或称为”是(Yes)、“否(No)相对应。

    5、用二进制表示数据具有抗干扰能力强,可靠性高等优点。因为每位数据只有高低两个状态,当受到一定程度的干扰时,仍能可靠地分辨出它是高还是低。

    二进制的运算

    今天我们讲的都是10进制转二进制,其他的就不在这篇文章中讲了,想了解的朋友可以私信我。十进制意思就是满10了就向前进1,比如 199加上1 那最后一位变成了10就要往前进1并且当前为变成0,而中间位是9,他加上低位进为的1了也变成了10也要往前进1,自己变成0,高位的1在加上中间位进的1就变成了2,所以最终的结果就是200。同理,二进制也是一样的,满二就进一,然后当前位变成0,比如10010101 用这个二进制来加1,他的进位为:

     

    当然还有一个概念是大家要明白的,那就是符号位,也就是高位,是二进制的最右边位。

    当高位为1的时候表示是一个负数,高位是0的时候表示的是正数。

    概念普及:真值,源码,反码,补码。

    真值:去掉符号以后数字的二进制。

    源码:符号位加上真值的绝对值, 用第一位表示符号。

    反码:符号位不变,其他的位数全部取反。

    补码:在反码的基础上加一。

    比如以上二进制(10010101 )的

    真值:0010101

    源码:10010101

    反码:11101010

    补码:11101011

    1. 二进制和十进制之间的转换

    首先是10进制转二进制

    计算方式是采取短除法(对于短除法不会的朋友就要自己查资料学习下了),短除法完毕以后余数从下往上读就是了。

    比如以下是十进制的16转为对应二进制的计算方式:

     

    十进制的13转对应的二进制:

     

    以上为纯粹的数学演示,如果是在计算机中,我们的位数就不能是只有4位了,因为计算机中存储的数字最低的都是8位,要么是16位或者是32位还有64位,比如以上二进制的写法标准的可以写成00010000(16的二进制),00001101(13的二进制)。为了方便后面我们都采用8位给大家演示,

    再看看二进制转10进制

    比如给定一个二进制00001001,我们要将其转化为十进制,计算方式是:十进制 = (二进制的当前位数值) 乘以 (2的当前位数次方-1),如下:

    符号位不参与运算,所以

    0001001 ==》 0*2^6 + 0*2^5 + 0*2^4 + 1*2^3 + 0*2^2 + 0*2^1 + 1*2^0

    最终相加就是0 + 0 + 0 + 0 + 8 + 0 + 0 + 1 = 9

    1. 与,或,非,异或及移位运算

    与运算,符号&同为1则位1,否则为0

    8 & 11:

    0 0 0 0 1 0 0 0

      &

    0 0 0 0 1 0 1 1

    ————————

    0 0 0 0 1 0 0 0

    结果为8

    -8 & 11:

    1 1 1 1 1 0 0 0 (补码)

     &

    0 0 0 0 1 0 1 1

    ————————

    0 0 0 0 1 0 0 0

    结果为8

    或运算,符号|:全0为0,否则为1

    8 | 11:

    00001000

     |

    00001011

    ————————

    00001011

    结果为 11

    -8 | 11:

    11111000(补码)

    |

    00001011

    -------------------

    11111011(补码)

    求得原码: 10000101

    结果为-5

    非运算,符号~:直接取反,遇1则0,遇0则1

    ~8:

    00001000

    ------------------------------

    11110111(得到补码)

     计算原码:10001001

     结果为 -9

    ~-8:

    11111000

    ——————————

    00000111

    结果为7

    异或运算,符号^:相同为0,不同为1

    8 ^ 11:

    00001000  

    ^

    00001011

    __________

    00000011

    结果为3

    再次与11进行异或:

    00000011 ^ 00001011 = 00001000 结果有变回8,结论:异或一个相同的数字偶数次,结果仍为这个数字

    有符号左移位<< 

    将运算数的二进制整体左移指定位数,低位用0补齐。

    正数:5<<2

    第一步:转化为二进制。

    000 0101

    第二步:整体左移指2位数,右边低位用0补齐。

    001 0100

    第三步:转化为十进制

    1*2^4+1*2^2=16+4=20

    M<<N相当于M*2^n.

    负数:-5<<2

    第一步:转化为二进制。

    1111 1011

    第二步:整体左移指2位数

    1110 1100

    这是一个负数算出他的补码:1001 0100

    第三步:转化为十进制

    -20

    M<<N相当于M*2^n.

    >>有符号右移位

    将运算数的二进制整体右移指定位数,正数高位用0补齐,负数高位用1补齐(保持负数符号不变)

    正数:5>>2

    第一步,转化为二进制。

    0000 0101

    第二步,整体右移指2位数

    0000 0001

    第三步,转化为十进制

    1*2^0=1

    1. >N相当于M/2^n 取商

    负数:-5>>2

    第一步,转化为二进制:

    1111 1011

    第二步,整体右移指2位数

    1111 1110

    第三步,转化为十进制

    -2

    M<<N相当于如果运算数是偶数,那么那么它的运算结果就是 x = -(|x| / 2),如果运算数是奇数,那么它的运算结果就是 x = -(|x| / 2) - 1

    >>>无符号右移位

    不管正数还是负数,高位都用0补齐(忽略符号位)

    一、正数的>>>无符号右移位和>>有符号右移位计算结果相同

    二、负数

    -5>>>2

    第一步,转化为二进制:

    1111 1111 1111 1111 1111 1111 1111 1011

    第二步,整体右移指2位数

    0011 1111 1111 1111 1111 1111 1111 1110

    第三步,转化为十进制

    1073741822

    展开全文
  • RFID-二进制树搜索

    千次阅读 热门讨论 2019-06-16 22:28:22
    MATLAB编程实现二进制树搜索与二分支搜索。

    说明

    MATLAB编程实现二进制树搜索与二分支搜索。

    二进制树搜索

    原理

    二进制搜索技术以唯一的序列号来识别射频电子标签为基础。为了从一簇射频电子标签中选择其中之一, 射频读写器发出一个读命令, 将射频电子标签序列号传输时的数据碰撞引导到射频读写器, 即由射频读写器判断是否有碰撞发生。 如果有, 则进一步搜索。在二进制算法的实现中, 起决定作用的是射频读写器所使用的信号编码必须能够确定碰撞发生的准确位置。
    二进制树搜索算法是由在一个射频读写器和多个射频电子标签之间规定的相互作用(命令和应答)顺序(规则)构成的,目的在于从较大的一组中选出一个标签。算法规定,射频电子标签之间要严格同步,以便准确地监测碰撞位的发生,并且要求能辨认出读写器中数据碰撞的比特位的准确位置,一般采用曼切斯特编码。
    为了实现这种算法,就需要一组命令,这组命令由射频读写器发出,可由射频电子标签处理,命令如下:
    REQUEST:此命令发送一序列号作为参数给射频电子标签。射频电子标签把自己的序列号与接收的序列号比较。如果小于或等于请求序列号,则此射频电子标签回送其序列号给射频读写器。这样就可以缩小预选的射频电子标签的范围请求;
    SELECT:用某个事先确定的序列号作为参数发送给射频电子标签。具有相同序列号的射频电子标签将此作为执行其他命令(例如读出和写入数据)的切入开关, 选择这个射频电子标签。具有其他序列号的射频电子标签只对REQUEST命令应答;
    READ-DATA:选中的射频电子标签将其存储的数据发送给射频读写器;
    UNSELECT:取消一个选中的射频电子标签,使射频电子标签进入“静默”状态, 这种状态中电于标签完全是非撤活的,对收到的REQUEST命令不作应答。为了更新激活射频电子标签,暂时离开射频读写器的作用范围(等于没有供应电压),以实现复位。

    代码

    • request
    %request函数
    %射频电子标签把自己的序列号与接收的序列号比较。如果小于或等于,返回。
    function [bit,D_max]=request(new_label,new_n)
    bit=ones(1,8);           %81
    for k=1:8
        sum=0;
        for j=1:new_n
            sum=sum+new_label(j,k);    
        end
        if sum==0           %即对应位全为0
            bit(1,k)=0;
        elseif sum==new_n   %对应为全为1
            bit(1,k)=1;
        else
            D_max=k;        %碰撞位,且设为0
            bit(1,k)=0;
            break;
        end
    end
    end
    
    • select
    %select函数
    %用某个事先确定的序列号作为参数发送给射频电子标签
    function [new_label2,new_n]=select(new_label,new_bit,new_max,n)
    new_n=0;
    for a=1:n
        if new_label(a,new_max)==new_bit(new_max)
            new_n=new_n+1;
            new_label2(new_n,:)=new_label(a,:);
        else
        end
    end
    end    
    
    • unselect
    %unselect函数
    function [h,nlabel]= unselect(new_label,nlabel,h)
    for nn=1:h
        result = isequal(new_label,nlabel(nn,:));
        if result 
            nlabel(nn,:)=[];  
            h=h-1;
            break;
        end
    end        
    end
    
    • 界面核心代码
    %主要代码位于pushbutton1_Callback
    
    % --- Executes on button press in pushbutton1.
    function pushbutton1_Callback(hObject, eventdata, handles)
    % hObject    handle to pushbutton1 (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    tic
    tag_count1=get(handles.edit1,'String');  %获取输入的标签总数
    ctag=str2num(tag_count1);              
    rand1=rand(ctag,1)*256;                  %随机生成标签
    rand2=dec2bin(rand1);                       
    tgrand=num2str(rand2);
    rand3=sort(rand1);                       
    rand4=dec2bin(rand3);                  
    rand5=num2str(rand4);
    toc                                      %记录程序运行时间
    set(handles.listbox1,'String',tgrand);   %输出
    set(handles.listbox2,'String',rand5);
    set(handles.edit2,'String',num2str(toc));
    

    界面结果

    在这里插入图片描述

    二分支

    原理

    该方法采用递归的工作方式,遇到碰撞就随机地进行分支,成为多个子集。在接下来的时隙中,主要解决信息包所发生的碰撞。如果再次发生碰撞,就继续随机地分为多个分支。然后依次进行查询,标签被分支后,由于响应的标签数量减少,再次发生碰撞的概率降低,通过不断的分支,减少响应标签的数量,直至分支内没有或仅有一个标签响应。

    1. 先随机生成16位的标签号,定义标签的左右子树;
    2. 先找出标签碰撞的位数,若得到碰撞的位,根据碰撞的位将标签号分为左右两子树(定义左子树位1,右子树位0);
    3. 判断左子树是否只有一个标签或无标签,若是,则该次调用结束,判断上步的右子树是否只有一个标签或无标签,若不是,则进入该右子树的左子树进行查验,以此循环。

    我们定义每一个左右子树都要进行一次查询和一次响应,因为可以计算出操作次数,并定义吞吐量等于标签数目除以总的操作次数。

    代码

    • pick函数
    %定义pick函数:碰撞算法
    function c=pick(A,tag_count,b,len)  %A为标签数组,tag_count为标签数目,b已经使用的时隙数,len为标签长度
    A
    tag_count
    m=1;                                %左子树标签数目
    n=1;                                %右子树标签数目
    b=b+2;                              %查询、响应
    D_max=len;                          %初始化最高碰撞位
    x=0;
    len_l=len;                          %初始化标签长度
    
    if tag_count==1                     %若标签数目为1,直接返回
        c=b;
        return;
    end
    
    for j=1:1:len_l                     %对标签每一位进行判断是否碰撞
        sum=0;
        for k=1:1:tag_count             %所有标签对应为求和
            x=A(k,j);
            sum=sum+x;
        end
        if sum==0                       %即对应为全为0,无碰撞
            ;     
        elseif sum==tag_count           %即对应为全为1,无碰撞
            ;
        else                            %碰撞
            D_max=j;                    %最高碰撞位
            break;
        end
    end
    for i=1:tag_count                   %把标签分成左、右子树
       x=A(i,D_max);
        if x==1                         %左子树为1
            left_tag(m,:)=A(i,:);
            m=m+1;                      %左子树+1
        else
            right_tag(n,:)=A(i,:);
            n=n+1;
        end
    end
    if m==2||m==1                      %左子树有一个标签或无标签则响应完毕
        b=b+2;
        c=b;
    else
        b=pick(left_tag,m-1,b,len);    %如果标签数大于2,则递归调用
    end
    
    if n==2||n==1                      %同上
        b=b+2;
        c=b;   
    else
        b=pick(right_tag,n-1,b,len);
    end
    c=b;                               %返回操作总的次数
    return;
    
    • 主函数核心代码
    %主要代码位于pushbutton1_Callback
    % --- Executes on button press in pushbutton1.
    function pushbutton1_Callback(hObject, eventdata, handles)
    % hObject    handle to pushbutton1 (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    ctag=str2num(get(handles.edit1,'String'));       %获取输入的标签总数
    ID_length=str2num(get(handles.edit2,'String'));  %获取输入的标签长度
    for i=1:ctag
        tagg(i,:)=randi([0,1],1,ID_length);          %随机生成tagcount个长为ID_length的标签
    end
    tag1 = unique(tagg,'rows');                      %去除重复
    tag_count=size(tag1,1);                          %计算不重复的标签总数
    r = randperm(tag_count);                         %打乱
    tag=tag1(r,:);
    tag;
    sum=0;a=0;
    a=pick(tag,tag_count,0,ID_length)                %调用pick函数
    sum=sum+a;                                       %计算操作次数
    cc=sum;
    tuntu=tag_count/cc;                              %计算吞吐量
    set(handles.edit4,'String',cc);                  %输出操作次数
    set(handles.edit5,'String',tuntu);               %输出吞吐量
    tag = num2str(tag); 
    tag;
    set(handles.listbox2,'String',tag);              %输出待识别的标签
    guidata(hObject,handles);
    
    % --- Executes on button press in pushbutton2.
    function pushbutton2_Callback(hObject, eventdata, handles)
    % hObject    handle to pushbutton2 (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    close;                                            %关闭窗口
    
    

    结果界面

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 二进制版opencl程序

    2019-06-26 09:35:01
    在前一篇介绍 program 等术语时,提到创建 program 对象有两种方式: ...区别仅在于 opencl 程序在用户面前的展现形式,前者是源代码形式,后者是二进制形式。二进制形式的数据格式是不透明...

    转载-原文地址:http://www.photoneray.com/opencl_04/

    在前一篇介绍 program 等术语时,提到创建 program 对象有两种方式: clCreateProgramWithSourceclCreateProgramWithBinary。区别仅在于 opencl 程序在用户面前的展现形式,前者是源代码形式,后者是二进制形式。二进制形式的数据格式是不透明的,不同的实现可以有不同的标准。使用二进制形式的好处有二:一是由于二进制码已经经过编译(部分编译为中间件或全部编译为可执行文件),所以加载速度更快,需要的内存更少;二是可以保护 opencl 代码,保护知识产权。

    下面我们就来看看如何利用二进制形式:


    存储 opencl 程序为二进制

    我们的第一个问题是:二进制版的 opencl 程序从哪里来?前文说过,所有的 cl 代码都要经过加载并创建 program 对象,然后由 program 对象在 device 上面编译并执行。难道还有其他方式编译 opencl 代码?答案是:NO!

    意味着我们还是需要将代码送到 device 里面编译。你会说,这不是多此一举吗?看起来确实有点,不过一般的做法是在软件安装的时候就进行编译保存二进制形式,然后真正运行时才加载二进制。这样分成两个步骤的话,倒也说的过去。

    省去前面那些与 platformdevicecontext的代码,我们直接进入创建 program 的地方。首先还是利用 clCreateProgramWithSource 函数读取源代码文件并用 clBuildProgram 函数编译。示例代码如下:

    cl_int status;
    cl_program program;
    
    ifstream kernelFile("binary_kernel.ocl", ios::in);
    if(!kernelFile.is_open())
    	return;
    
    ostringstream oss;
    oss << kernelFile.rdbuf();
    
    string srcStdStr = oss.str();
    const char *srcStr = srcStdStr.c_str();
    
    program = clCreateProgramWithSource(context, 
    									1, 
    									(const char **)&srcStr,
    									NULL,
    									NULL);
    
    if(program ==NULL)
    	return;
    
    status = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
    
    if(status != CL_SUCCESS)
    	return;
    

    代码可能不完整,完整示例请看文末。

    现在我们已经将 opencl 代码在 device 编译完成了。接下来要做的就是将编译好的二进制取出来存在磁盘上。使用的 API 就是 clGetProgramInfo:

    cl_int clGetProgramInfo(cl_program program,
    						cl_program_info param_name,
    						size_t param_value_size,
    						void *param_value,
    						size_t *param_value_size_ret)
    

    使用方法见如下代码片段(为使逻辑清晰,省略了错误检测,实际开发可不要省啊):

    cl_uint numDevices = 0;
    
    // 获取 program 绑定过的 device 数量
    clGetProgramInfo(program,
    				CL_PROGRAM_NUM_DEVICES,
    				sizeof(cl_uint),
    				&numDevices,
    				NULL);
    
    // 获取所有的 device ID
    cl_device_id *devices = new cl_device_id[numDevices];
    clGetProgramInfo(program,
    				CL_PROGRAM_DEVICES,
    				sizeof(cl_device_id) * numDevices,
    				devices,
    				NULL);
    
    // 决定每个 program 二进制的大小
    size_t *programBinarySizes = new size_t[numDevices];
    clGetProgramInfo(program,
    				CL_PROGRAM_BINARY_SIZES,
    				sizeof(size_t) * numDevices,
    				programBinarySizes,
    				NULL);
    
    unsigned char **programBinaries = new unsigned char *[numDevices];
    for(cl_uint i = 0; i < numDevices; ++i)
    	programBinaries[i] = new unsigned char[programBinarySizes[i]];
    
    // 获取所有的 program 二进制
    clGetProgramInfo(program,
    				CL_PROGRAM_BINARIES,
    				sizeof(unsigned char *) * numDevices,
    				programBinaries,
    				NULL);
    
    // 存储 device 所需要的二进制
    for(cl_uint i = 0; i < numDevices; ++i){
    	// 只存储 device 需要的二进制,多个 device 需要存储多个二进制
    	if(devices[i] == device){
    		FILE *fp = fopen("kernel_binary_ocl.bin", "wb"); 
    		fwrite(programBinaries[i], 1, programBinarySizes[i], fp);
    		fclose(fp);
    		break;
    	}
    }
    
    // 清理
    delete[] devices;
    delete [] programBinarySizes;
    for(cl_uint i = 0; i < numDevices; ++i)
    	delete [] programBinaries[i];
    delete[] programBinaries;
    

    要注意的是,可能有很多个 device 都编译了 program,所以将二进制提取出来时,我们是遍历了所有编译了 programdevice


    读取二进制版opencl程序

    经过上面一系列的操作,我们的磁盘上应该存在一个二进制版的 opencl 程序了。里面的内容可能是可读的,也可能是不可读的。这个视不同厂商实现而不同。

    相对于存储,读取看起来就清爽的多,无非是打开二进制文件,然后调用 clCreateProgramWithBinary函数。示例如下:

    FILE *fp= fopen("kernel_binary_ocl.bin", "rb");
    
    // 获取二进制的大小
    size_t binarySize;
    fseek(fp, 0, SEEK_END);
    binarySize = ftell(fp);
    rewind(fp);
    
    // 加载二进制文件
    unsigned char *programBinary = new unsigned char[binarySize];
    fread(programBinary, 1, binarySize, fp);
    fclose(fp);
    
    cl_program program;
    program = clCreateProgramWithBinary(context,
    									1,
    									&device,
    									&binarySize,
    									(const unsigned char**)&programBinary,
    									NULL,
    									NULL);
    
    delete [] programBinary;
    
    clBuildProgram(program 0, NULL, NULL, NULL, NULL);
    

    这里要注意,即使加载是二进制版,我们在之后还是要对其进行 clBuildProgram。原因在于,我们无法保证所谓的二进制一定是可执行码。因为每个厂商的实现不一,有的可能就是最终执行码,而有的却是中间码。所以无论是从源代码还是二进制创建 program,之后都需要 clBuildProgram

    这样兜了一圈,发现要使用二进制版还是用了一遍源代码方式,感觉代码复杂好多,有点多余。其实换个角度来看,我们完全可以写成两个程序,一个专门用来读取源代码并编译生成二进制,另一个才是读取二进制运行软件。前者开发人员使用,后者才是给用户使用的。只有这样才能体现二进制版的优势。


    示例代码

    OpenCLCompileToBin.cpp

    
    /*  OpenCLCompileToBin.cpp
     *  (c) by keyring <keyrings@163.com>
     *  2016.01.13
     */
    
    #include <iostream>
    
    #if defined(__APPLE__) || defined(__MACOSX)
    #include <OpenCL/opencl.h>
    #else
    #include <CL/cl.h>
    #endif
    
    
    #define KERNEL(...)#__VA_ARGS__
    
    const char *kernelSourceCode = KERNEL(
                                       __kernel void hellocl(__global uint *buffer)
    {
        size_t gidx = get_global_id(0);
        size_t gidy = get_global_id(1);
        size_t lidx = get_local_id(0);
        buffer[gidx + 4 * gidy] = (1 << gidx) | (0x10 << gidy);
    
    }
                                   );
    
    int main(int argc, char const *argv[])
    {
        printf("hello OpenCL\n");
        cl_int status = 0;
        size_t deviceListSize;
    
        // 得到并选择可用平台
        cl_uint numPlatforms;
        cl_platform_id platform = NULL;
        status = clGetPlatformIDs(0, NULL, &numPlatforms);
    
        if (status != CL_SUCCESS) {
            printf("ERROR: Getting Platforms.(clGetPlatformIDs)\n");
            return EXIT_FAILURE;
        }
    
        if (numPlatforms > 0) {
            cl_platform_id *platforms = (cl_platform_id *)malloc(numPlatforms * sizeof(cl_platform_id));
            status = clGetPlatformIDs(numPlatforms, platforms, NULL);
            if (status != CL_SUCCESS) {
                printf("Error: Getting Platform Ids.(clGetPlatformIDs)\n");
                return -1;
            }
    
            for (unsigned int i = 0; i < numPlatforms; ++i) {
                char pbuff[100];
                status = clGetPlatformInfo(
                             platforms[i],
                             CL_PLATFORM_VENDOR,
                             sizeof(pbuff),
                             pbuff,
                             NULL);
                platform = platforms[i];
                if (!strcmp(pbuff, "Advanced Micro Devices, Inc.")) {
                    break;
                }
            }
    
            delete platforms;
        }
    
        // 如果我们能找到相应平台,就使用它,否则返回NULL
        cl_context_properties cps[3] = {
            CL_CONTEXT_PLATFORM,
            (cl_context_properties)platform,
            0
        };
    
        cl_context_properties *cprops = (NULL == platform) ? NULL : cps;
    
    
        // 生成 context
        cl_context context = clCreateContextFromType(
                                 cprops,
                                 CL_DEVICE_TYPE_GPU,
                                 NULL,
                                 NULL,
                                 &status);
        if (status != CL_SUCCESS) {
            printf("Error: Creating Context.(clCreateContexFromType)\n");
            return EXIT_FAILURE;
        }
    
        // 寻找OpenCL设备
    
        // 首先得到设备列表的长度
        status = clGetContextInfo(context,
                                  CL_CONTEXT_DEVICES,
                                  0,
                                  NULL,
                                  &deviceListSize);
        if (status != CL_SUCCESS) {
            printf("Error: Getting Context Info device list size, clGetContextInfo)\n");
            return EXIT_FAILURE;
        }
        cl_device_id *devices = (cl_device_id *)malloc(deviceListSize);
        if (devices == 0) {
            printf("Error: No devices found.\n");
            return EXIT_FAILURE;
        }
    
        // 现在得到设备列表
        status = clGetContextInfo(context,
                                  CL_CONTEXT_DEVICES,
                                  deviceListSize,
                                  devices,
                                  NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Getting Context Info (device list, clGetContextInfo)\n");
            return EXIT_FAILURE;
        }
    
    
        // 装载内核程序,编译CL program ,生成CL内核实例
    
        size_t sourceSize[] = {strlen(kernelSourceCode)};
        cl_program program = clCreateProgramWithSource(context,
                             1,
                             &kernelSourceCode,
                             sourceSize,
                             &status);
        if (status != CL_SUCCESS) {
            printf("Error: Loading Binary into cl_program (clCreateProgramWithBinary)\n");
            return EXIT_FAILURE;
        }
    
        // 为指定的设备编译CL program.
        status = clBuildProgram(program, 1, devices, NULL, NULL, NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Building Program (clBuildingProgram)\n");
            return EXIT_FAILURE;
        }
        deviceListSize = 1;
        // 决定每个 program 二进制的大小
        size_t *programBinarySizes = new size_t[deviceListSize];
        clGetProgramInfo(program,
                        CL_PROGRAM_BINARY_SIZES,
                        sizeof(size_t) * deviceListSize,
                        programBinarySizes,
                        NULL);
    
        printf("%lu\n", deviceListSize);
        unsigned char **programBinaries = new unsigned char *[deviceListSize];
        for(cl_uint i = 0; i < deviceListSize; ++i)
            programBinaries[i] = new unsigned char[programBinarySizes[i]];
    
        // 获取所有的 program 二进制
        clGetProgramInfo(program,
                        CL_PROGRAM_BINARIES,
                        sizeof(unsigned char *) * deviceListSize,
                        programBinaries,
                        NULL);
    
        printf("ready write to file\n");
        // 写入文件
        FILE *fp = fopen("./kernel_binary_ocl.bin", "wb"); 
        fwrite(programBinaries[0], 1, programBinarySizes[0], fp);
        fclose(fp);
    
    
        // 资源回收
    
        status = clReleaseProgram(program);
        status = clReleaseContext(context);
    
        free(devices);
        delete [] programBinarySizes;
        for(cl_uint i = 0; i < deviceListSize; ++i)
            delete [] programBinaries[i];
        delete programBinaries;
    
        return 0;
    }
    
    

    OpenCLRunWithBin.cpp

    
    /*  OpenCLRunWithBin.cpp
     *  (c) by keyring <keyrings@163.com>
     *  2016.01.13
     */
    
    #include <iostream>
    
    #if defined(__APPLE__) || defined(__MACOSX)
    #include <OpenCL/opencl.h>
    #else
    #include <CL/cl.h>
    #endif
    
    
    // #define KERNEL(...)#__VA_ARGS__
    
    // const char *kernelSourceCode = KERNEL(
    //                                    __kernel void hellocl(__global uint *buffer)
    // {
    //     size_t gidx = get_global_id(0);
    //     size_t gidy = get_global_id(1);
    //     size_t lidx = get_local_id(0);
    //     buffer[gidx + 4 * gidy] = (1 << gidx) | (0x10 << gidy);
    
    // }
    //                                );
    
    int main(int argc, char const *argv[])
    {
        printf("hello OpenCL\n");
        cl_int status = 0;
        size_t deviceListSize;
    
        // 得到并选择可用平台
        cl_uint numPlatforms;
        cl_platform_id platform = NULL;
        status = clGetPlatformIDs(0, NULL, &numPlatforms);
    
        if (status != CL_SUCCESS) {
            printf("ERROR: Getting Platforms.(clGetPlatformIDs)\n");
            return EXIT_FAILURE;
        }
    
        if (numPlatforms > 0) {
            cl_platform_id *platforms = (cl_platform_id *)malloc(numPlatforms * sizeof(cl_platform_id));
            status = clGetPlatformIDs(numPlatforms, platforms, NULL);
            if (status != CL_SUCCESS) {
                printf("Error: Getting Platform Ids.(clGetPlatformIDs)\n");
                return -1;
            }
    
            for (unsigned int i = 0; i < numPlatforms; ++i) {
                char pbuff[100];
                status = clGetPlatformInfo(
                             platforms[i],
                             CL_PLATFORM_VENDOR,
                             sizeof(pbuff),
                             pbuff,
                             NULL);
                platform = platforms[i];
                if (!strcmp(pbuff, "Advanced Micro Devices, Inc.")) {
                    break;
                }
            }
    
            delete platforms;
        }
    
        // 如果我们能找到相应平台,就使用它,否则返回NULL
        cl_context_properties cps[3] = {
            CL_CONTEXT_PLATFORM,
            (cl_context_properties)platform,
            0
        };
    
        cl_context_properties *cprops = (NULL == platform) ? NULL : cps;
    
    
        // 生成 context
        cl_context context = clCreateContextFromType(
                                 cprops,
                                 CL_DEVICE_TYPE_GPU,
                                 NULL,
                                 NULL,
                                 &status);
        if (status != CL_SUCCESS) {
            printf("Error: Creating Context.(clCreateContexFromType)\n");
            return EXIT_FAILURE;
        }
    
        // 寻找OpenCL设备
    
        // 首先得到设备列表的长度
        status = clGetContextInfo(context,
                                  CL_CONTEXT_DEVICES,
                                  0,
                                  NULL,
                                  &deviceListSize);
        if (status != CL_SUCCESS) {
            printf("Error: Getting Context Info device list size, clGetContextInfo)\n");
            return EXIT_FAILURE;
        }
        cl_device_id *devices = (cl_device_id *)malloc(deviceListSize);
        if (devices == 0) {
            printf("Error: No devices found.\n");
            return EXIT_FAILURE;
        }
    
        // 现在得到设备列表
        status = clGetContextInfo(context,
                                  CL_CONTEXT_DEVICES,
                                  deviceListSize,
                                  devices,
                                  NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Getting Context Info (device list, clGetContextInfo)\n");
            return EXIT_FAILURE;
        }
    
        FILE *fp= fopen("./kernel_binary_ocl.bin", "rb");
    
        // 获取二进制的大小
        size_t binarySize;
        fseek(fp, 0, SEEK_END);
        binarySize = ftell(fp);
        rewind(fp);
    
        // 加载二进制文件
        unsigned char *programBinary = new unsigned char[binarySize];
        fread(programBinary, 1, binarySize, fp);
        fclose(fp);
    
        cl_program program;
        program = clCreateProgramWithBinary(context,
                                            1,
                                            &devices[0],
                                            &binarySize,
                                            (const unsigned char**)&programBinary,
                                            NULL,
                                            NULL);
    
        delete [] programBinary;
    
        // 装载内核程序,编译CL program ,生成CL内核实例
    
        // size_t sourceSize[] = {strlen(kernelSourceCode)};
        // cl_program program = clCreateProgramWithSource(context,
        //                      1,
        //                      &kernelSourceCode,
        //                      sourceSize,
        //                      &status);
        // if (status != CL_SUCCESS) {
        //     printf("Error: Loading Binary into cl_program (clCreateProgramWithBinary)\n");
        //     return EXIT_FAILURE;
        // }
    
        // 为指定的设备编译CL program.
        status = clBuildProgram(program, 1, devices, NULL, NULL, NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Building Program (clBuildingProgram)\n");
            return EXIT_FAILURE;
        }
    
        // 得到指定名字的内核实例的句柄
        cl_kernel kernel = clCreateKernel(program, "hellocl", &status);
        if (status != CL_SUCCESS) {
            printf("Error: Creating Kernel from program.(clCreateKernel)\n");
            return EXIT_FAILURE;
        }
    
        // 创建 OpenCL buffer 对象
        unsigned int *outbuffer = new unsigned int [4 * 4];
        memset(outbuffer, 0, 4 * 4 * 4);
        cl_mem outputBuffer = clCreateBuffer(
            context, 
            CL_MEM_ALLOC_HOST_PTR, 
            4 * 4 * 4, 
            NULL, 
            &status);
    
        if (status != CL_SUCCESS) {
            printf("Error: Create Buffer, outputBuffer. (clCreateBuffer)\n");
            return EXIT_FAILURE;
        }
    
    
        //  为内核程序设置参数
        status = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&outputBuffer);
        if (status != CL_SUCCESS) {
            printf("Error: Setting kernel argument. (clSetKernelArg)\n");
            return EXIT_FAILURE;
        }
    
        // 创建一个OpenCL command queue
        cl_command_queue commandQueue = clCreateCommandQueue(context,
                                        devices[0],
                                        0,
                                        &status);
        if (status != CL_SUCCESS) {
            printf("Error: Create Command Queue. (clCreateCommandQueue)\n");
            return EXIT_FAILURE;
        }
    
    
        // 将一个kernel 放入 command queue
        size_t globalThreads[] = {4, 4};
        size_t localThreads[] = {2, 2};
        status = clEnqueueNDRangeKernel(commandQueue, kernel,
                                        2, NULL, globalThreads,
                                        localThreads, 0,
                                        NULL, NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Enqueueing kernel\n");
            return EXIT_FAILURE;
        }
    
        // 确认 command queue 中所有命令都执行完毕
        status = clFinish(commandQueue);
        if (status != CL_SUCCESS) {
            printf("Error: Finish command queue\n");
            return EXIT_FAILURE;
        }
    
        // 将内存对象中的结果读回Host
        status = clEnqueueReadBuffer(commandQueue,
                                     outputBuffer, CL_TRUE, 0,
                                     4 * 4 * 4, outbuffer, 0, NULL, NULL);
        if (status != CL_SUCCESS) {
            printf("Error: Read buffer queue\n");
            return EXIT_FAILURE;
        }
    
        // Host端打印结果
        printf("out:\n");
        for (int i = 0; i < 16; ++i) {
            printf("%x ", outbuffer[i]);
            if ((i + 1) % 4 == 0)
                printf("\n");
        }
    
        // 资源回收
        status = clReleaseKernel(kernel);
        status = clReleaseProgram(program);
        status = clReleaseMemObject(outputBuffer);
        status = clReleaseCommandQueue(commandQueue);
        status = clReleaseContext(context);
    
        free(devices);
        delete outbuffer;
    
        system("pause");
    
        return 0;
    }
    
    

    先使用compile文件编译一个bin,然后使用run文件加载bin运行。

    展开全文
  • 计算机采用二进制码的优点

    万次阅读 2018-05-12 15:28:40
    1.易于物理实现;2.二进制运算简单;3.机器可靠性高;4通用性强。
  • bcd码二进制转十进制Prerequisite: Number systems 先决条件: 数字系统 BCD Code (8421 Code): In BCD 8421 code, each decimal digit is represented using a 4-bit binary number. The 4-bit binary numbers ...
  • 为了提高图像局部特征算法的计算速率与匹配速度,并保持其准确率,提出了一种局部映射二进制串描述符...从实验结果表明,只需32比特的二进制串就可以在准确率上媲美现有的局部描述符算法,并在匹配速度上有较大的优势
  • 使用二进制来表示数据状态

    千次阅读 2020-04-30 14:51:44
    使用二进制的方式来表示数据状态(支持无顺序状态) 文章目录使用二进制的方式来表示数据状态(支持无顺序状态)1. 背景介绍2. 通过一个案例引发思考2.1 当签章有顺序时,我们是如何设计的?2.2 当签章顺序无法控制...
  • 二进制运算

    千次阅读 2019-03-10 16:27:06
    1.十进制转化为二进制(编译器为32进制) #include&amp;amp;lt;iostream&amp;amp;gt; using namespace std; int main() { int m,number ,s[32]; cin&amp;amp;gt;&amp;amp;gt;number; for...
  • 二进制协议与文本协议

    千次阅读 2019-05-08 09:51:00
    二进制协议 VS 文本协议 前言 最近由于工作上的需要(一方面是与底层与传感器进行数据交互,另一方面是对RabbitMQ的AMQP协议的学习),接触了一些网络协议相关的内容。正好就二进制协议与文本协议的一些问题简单说...
  • Go打包二进制文件

    2020-12-15 09:32:28
    众所周知,go语言可打包成目标平台二进制文件是其一大优势,如此go项目在服务器不需要配置go环境和依赖就可跑起来。 2 操作 2.1 打包部署到centos7 2.1.1 打包环境:mac os 方法:进入main.go所在目录,输入如下命令...
  • 二进制编码的十进制

    千次阅读 2020-08-25 14:32:35
    二进制编码的十进制数是由于计算机CPU(中央处理单元)的二进制性质而没有舍入错误的精确数字。 这使得bcd数据类型特别适合于财务和簿记目的。 BCD calculations have been present in computer science for quite ...
  • 网络的划分和二进制

    2020-10-25 16:44:21
    这里写目录标题1....数字是信号的优势是看干扰能力强以及传输距离源并能保证质量 模拟信号和数字信号的根本区别就是传输的形式不一样,数字信号要比模拟信号稳定。数字信号比模拟信号好用多了,更加清晰,接受信号
  • 目录写在前面正文快速认识实现方式一实现方式写在最后写在前面FPGA基础知识极简教程(9)讲到了七段数码管的显示Verilog设计,我们都知道,要在数码管上显示的数字,使用BCD编码是具有优势的(或者是最正确的)。...
  • 十六进制优势所在:1 与二进制转换方便,记住:8、4、2、1. "8421"2 数字长度比二进制短,与10进制相当。实际上在计算机内部使用的数字只有2进制的,也就是说只有0和1两个数字而已。 而16进制是计算机使用人员为了更好...
  • k8s二进制集群中添加/新增node节点

    千次阅读 2019-10-17 09:35:50
    @[TOC](k8s实践(三) 二进制集群中添加node节点) 原有环境说明 主机名 系统版本 ip地址 docker version kubelet version kubeadm version kubectl version flannel version 备注 master centos-release-7-7....
  • 探讨二进制Edwards 曲线上的点压缩算法。分别给出了二进制Edwards 曲线的单点压缩算法和两点压缩算法。平均压缩点带宽为nbit 和1. 5n - 1 bit,平均恢复点开销为I + 0. 5M + S和1. 5I + M + S. 通过与对其他类型...
  • 简单的二进制编码

    2020-04-20 16:08:05
    在这个疯狂的世界中,我们仍然看到大量使用ASCII编码的表示形式,例如FIX标签值,以及一些更合理的二进制编码的表示形式,例如FAST 。 有些市场甚至犯下了以XML发送市场数据的罪过! 好吧,我不能抱怨太多,因为...
  • Pillow image转二进制

    千次阅读 2020-04-03 11:08:58
    经常碰见图像转二进制的问题,总忘 有种方法需要将图片保存下来,然后再以二进制的方式读取,但这样显然会有io问题,关键的这图片下载下来也没用 使用管道可以避免存储到硬盘的问题 优势 在内存操作,速度快 ...
  • K8S的二进制包安装步骤

    千次阅读 2020-08-31 22:53:33
    K8S的二进制包安装步骤1.前置条件1.1 准备3台机器1.2 关闭防火墙1.3 下载Kubernetes二进制包2. Master节点的配置2.1 安装docker2.2 安装etcd服务2.2.1 下载etcd二进制文件2.2.2 解压拷贝2.2.3 配置systemd服务文件...
  • 计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异。 简单来说,文本文件是基于字符编码的文件,常见的编码有ASCII编码,UNICODE编码...
  • 如果从 5 个元素中任意取 N 个元素形成组合的话,用二进制位来表示每个元素是否被放到组合里,就是: 看到这里,应该就非常清楚了吧,每种组合都可以拆解为 N 个二进制位的表达形式,而每个二进制组合同时代表着一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 114,331
精华内容 45,732
关键字:

二进制的优势