计科院 (今日 3

计算机科学的基本理论、工程技术和前沿话题。




原始的帖子在这里,电路原理图也有 https://bbs.kechuang.org/t/81422 现在重新做了几块板子,挺漂亮的。 ============================================== 计算机系统的配置: 小名:qCPU(CuteCPU) CPU:4位 ROM:4KB RAM:4KB 运行频率:74芯片极限 IO:2组4位的IO口 4个按键 其他外设就是5个LED发光二极管,一个蜂鸣器,一个4位数码管, 一个5X7点阵LED,一个LCD1602液晶 配备我自己设计的汇编器,使用起来跟 当年使用8086汇编编程很像 目前可以使用USB下载,USB提供电源,在电脑编写代码后,编译下载运行一条龙!! ============================================== 支持指令如下: ADD    加法 RM     读取内存 ADDC   带进位加法 WM     写内存 SUB    减法 OUT    IO输出 <      小于比较 =      等于比较 GOTO   小跳转 QCLR   清空 QMOV   赋值 QADD   加法 QSUB   减法 QGOTO  长跳转 QJMP   立即跳转 ============================================== 主板整体图 LED点阵和数码管板子 漂亮但是也比较贵的圆孔IC插座,还没有安装上74芯片的样子 漂亮的白色USB下载器 这个是原始的电路版本和USB下载器,可以比较一下 新版本合影,还有LED点阵和数码管没有做好,点阵是方形点阵 非常好用的,自己设计的汇编器 ============================================== 一共是: 一块蓝色主板, 一块白色下载器, 一块1602液晶, 一块LED点阵 一块数码管 一起大概170元,(如果不要圆孔IC插座150元这样就可以,龙少的设计好像400多元) ============================================== CuteCPU计算机原理图-2.pdf 587k 99次 CuteCPU指令集解释.pdf 89.4k 65次 LED点阵等原理图.pdf 233k 41次 CuteCPU编译器.rar 239k 55次 xLED流水灯测试代码.a 167b 36次 LED点阵马赛克.a 775b 30次 LED点阵显示0-9(2).a 11.6k 30次


2006版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考(A-M/N-Z) 第三卷:系统编程指南(Part1/2) 附卷:优化参考 注意2006版已经包括了32位和64位架构,只不过64位架构被称为IA-32e。 Intel开发手册2006.rar 10.1M 36次 2001版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考 第三卷:系统编程指南 注意2001版只更新到了奔腾4,也就是最高只有32位架构,并且扩展指令集只到SSE2.0。 Intel开发手册2001.zip 7.79M 3次 1999版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考 第三卷:系统编程指南 注意1999版只更新到了奔腾III,也就是最高只有32位架构,并且扩展指令集只到SSE1.0。 intel开发手册1999.zip 7.64M 2次 目前找到的中文翻译版,来自网络,均为第三卷部分章节翻译: IA-32卷3:系统编程指南.pdf 1.55M 5次 INTEL开发手册卷3(中文版).pdf 1.99M 7次 现在的版本可以在这里下载: https://software.intel.com/en-us/articles/intel-sdm


Bitcoin: A Peer-to-Peer Electronic Cash System 中本聪(Satoshi Nakamoto) 原文: www.bitcoin.org ,翻译: www.8btc.com [摘要]:本文提出了一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名(Digital signatures)部分解决了这个问题,但是如果仍然需要第三方的支持才能防止双重支付(double-spending)的话,那么这种系统也就失去了存在的价值。我们(we)在此提出一种解决方案,使现金系统在点对点的环境下运行,并防止双重支付问题。该网络通过随机散列(hashing)对全部交易加上时间戳(timestamps),将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列(sequence)的证明,而且被看做是来自CPU计算能力最大的池(pool)。只要大多数的CPU计算能力都没有打算合作起来对全网进行攻击,那么诚实的节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点(nodes)可以随时离开和重新加入网络,并将最长的工作量证明链条作为在该节点离线期间发生的交易的证明。 1. 引言 互联网上的贸易,几乎都需要借助金融机构作为可资信赖的第三方来处理电子支付信息。虽然在绝大多数情况下这类系统都运作良好,但是这类系统仍然内生性地受制于“基于信用的模式”(trust based model)的弱点。人们无法实现完全不可逆的交易,因为金融机构总是不可避免地会出面协调争端。而金融中介的存在,也会增加交易的成本,并且限制了实际可行的最小交易规模,也限制了日常的小额支付交易。并且潜在的损失还在于,很多商品和服务本身是无法退货的,如果缺乏不可逆的支付手段,互联网的贸易就大大受限。因为有潜在的退款的可能,就需要交易双方拥有信任。此外,因为商家也必须对自己的客户小心提防,所以会向客户索取完全不必要的个人信息。而实际的商业行为中,一定比例的欺诈性客户也被认为是不可避免的,相关损失视作销售费用处理。而在使用物理现金的情况下,因为此时没有第三方信用中介的存在,这些销售费用和支付问题上的不确定性却是可以避免的。 所以,我们非常需要这样一种电子支付系统,它基于密码学原理而不基于信用,使得任何达成一致的双方,能够直接进行支付,从而不需要第三方中介的参与。杜绝回滚(reverse)支付交易的可能,这就可以保护特定的卖家免于欺诈;而对于想要保护买家的人来说,在此环境下设立通常的第三方担保机制也可谓轻松加愉快。在这篇论文中,我们(we)将提出一种通过点对点分布式的时间戳服务器来生成依照时间前后排列并加以记录的电子交易证明,从而解决双重支付问题。只要诚实的节点所控制的计算能力的总和,大于有合作关系的(cooperating)攻击者的计算能力的总和,该系统就是安全的。 2. 交易(Transactions) 我们定义,一枚电子货币(an electronic coin)是这样的一串数字签名:每一位所有者通过对前一次交易和下一位拥有者的公钥(Public key) 签署一个随机散列的数字签名,并将这个签名附加在这枚电子货币的末尾,电子货币就发送给了下一位所有者。而收款人通过对签名进行检验,就能够验证该链条的所有者。 该过程的问题在于,收款人将难以检验,之前的某位所有者,是否对这枚电子货币进行了双重支付。通常的解决方案,就是引入信得过的第三方权威,或者类似于造币厂(mint)的机构,来对每一笔交易进行检验,以防止双重支付。在每一笔交易结束后,这枚电子货币就要被造币厂回收,而造币厂将发行一枚新的电子货币;而只有造币厂直接发行的电子货币,才算作有效,这样就能够防止双重支付。可是该解决方案的问题在于,整个货币系统的命运完全依赖于运作造币厂的公司,因为每一笔交易都要经过该造币厂的确认,而该造币厂就好比是一家银行。 我们需要收款人有某种方法,能够确保之前的所有者没有对更早发生的交易实施签名。从逻辑上看,为了达到目的,实际上我们需要关注的只是于本交易之前发生的交易,而不需要关注这笔交易发生之后是否会有双重支付的尝试。为了确保某一次交易是不存在的,那么唯一的方法就是获悉之前发生过的所有交易。在造币厂模型里面,造币厂获悉所有的交易,并且决定了交易完成的先后顺序。如果想要在电子系统中排除第三方中介机构,那么交易信息就应当被公开宣布(publicly announced)1,我们需要整个系统内的所有参与者,都有唯一公认的历史交易序列。收款人需要确保在交易期间绝大多数的节点都认同该交易是首次出现。 3. 时间戳服务器(Timestamp server) 本解决方案首先提出一个“时间戳服务器”。时间戳服务器通过对以区块(block)形式存在的一组数据实施随机散列而加上时间戳,并将该随机散列进行广播,就像在新闻或世界性新闻组网络(Usenet)的发帖一样2345。显然,该时间戳能够证实特定数据必然于某特定时刻是的确存在的,因为只有在该时刻存在了才能获取相应的随机散列值。每个时间戳应当将前一个时间戳纳入其随机散列值中,每一个随后的时间戳都对之前的一个时间戳进行增强(reinforcing),这样就形成了一个链条(Chain)。 4. 工作量证明(Proof-of-Work) 为了在点对点的基础上构建一组分散化的时间戳服务器,仅仅像报纸或世界性新闻网络组一样工作是不够的,我们还需要一个类似于亚当·柏克(Adam Back)提出的哈希现金(Hashcash)6。在进行随机散列运算时,工作量证明机制引入了对某一个特定值的扫描工作,比方说SHA-256下,随机散列值以一个或多个0开始。那么随着0的数目的上升, 找到这个解所需要的工作量将呈指数增长,但是检验结果仅需要一次随机散列运算。 我们在区块中补增一个随机数(Nonce),这个随机数要使得该给定区块的随机散列值出现了所需的那么多个0。我们通过反复尝试来找到这个随机数,找到为止。这样我们就构建了一个工作量证明机制。只要该CPU耗费的工作量能够满足该工作量证明机制,那么除非重新完成相当的工作量,该区块的信息就不可更改。由于之后的区块是链接在该区块之后的,所以想要更改该区块中的信息,就还需要重新完成之后所有区块的全部工作量。 同时,该工作量证明机制还解决了在集体投票表决时,谁是大多数的问题。如果决定大多数的方式是基于IP地址的,一IP地址一票,那么如果有人拥有分配大量IP地址的权力,则该机制就被破坏了。而工作量证明机制的本质则是一CPU一票。“大多数”的决定表达为最长的链,因为最长的链包含了最大的工作量。如果大多数的CPU为诚实的节点控制,那么诚实的链条将以最快的速度延长,并超越其他的竞争链条。如果想要对业已出现的区块进行修改,攻击者必须重新完成该区块的工作量外加该区块之后所有区块的工作量,并最终赶上和超越诚实节点的工作量。我们将在后文证明,设想一个较慢的攻击者试图赶上随后的区块,那么其成功概率将呈指数化递减。 另一个问题是,硬件的运算速度在高速增长,且节点参与网络的程度会有所起伏。为了解决这个问题,工作量证明的难度(the proof-of-work difficulty)将采用移动平均目标的方法来确定,即令难度指向令每小时生成区块的速度为某一预设的平均数。如果区块生成的速度过快,那么难度就会提高。 5. 网络 运行该网络的步骤如下: 1) 新的交易向全网进行广播; 2) 每一个节点都将收到的交易信息纳入一个区块中; 3) 每个节点都尝试在自己的区块中找到一个具有足够难度的工作量证明; 4) 当一个节点找到了一个工作量证明,它就向全网进行广播; 5) 当且仅当包含在该区块中的所有交易都是有效的且之前未存在过的,其他节点才认同该区块的有效性; 6) 其他节点表示他们接受该区块,而表示接受的方法,则是在跟随该区块的末尾,制造新的区块以延长该链条,而将被接受区块的随机散列值视为先于新区快的随机散列值。 节点始终都将最长的链条视为正确的链条,并持续工作和延长它。如果有两个节点同时广播不同版本的新区块,那么其他节点在接收到该区块的时间上将存在先后差别。当此情形,他们将在率先收到的区块基础上进行工作,但也会保留另外一个链条,以防后者变成最长的链条。该僵局(tie)的打破要等到下一个工作量证明被发现,而其中的一条链条被证实为是较长的一条,那么在另一条分支链条上工作的节点将转换阵营,开始在较长的链条上工作。 所谓“新的交易要广播”,实际上不需要抵达全部的节点。只要交易信息能够抵达足够多的节点,那么他们将很快被整合进一个区块中。而区块的广播对被丢弃的信息是具有容错能力的。如果一个节点没有收到某特定区块,那么该节点将会发现自己缺失了某个区块,也就可以提出自己下载该区块的请求。 6. 激励 我们约定如此:每个区块的第一笔交易进行特殊化处理,该交易产生一枚由该区块创造者拥有的新的电子货币。这样就增加了节点支持该网络的激励,并在没有中央集权机构发行货币的情况下,提供了一种将电子货币分配到流通领域的一种方法。这种将一定数量新货币持续增添到货币系统中的方法,非常类似于耗费资源去挖掘金矿并将黄金注入到流通领域。此时,CPU的时间和电力消耗就是消耗的资源。 另外一个激励的来源则是交易费(transaction fees)。如果某笔交易的输出值小于输入值,那么差额就是交易费,该交易费将被增加到该区块的激励中。只要既定数量的电子货币已经进入流通,那么激励机制就可以逐渐转换为完全依靠交易费,那么本货币系统就能够免于通货膨胀。 激励系统也有助于鼓励节点保持诚实。如果有一个贪婪的攻击者能够调集比所有诚实节点加起来还要多的CPU计算力,那么他就面临一个选择:要么将其用于诚实工作产生新的电子货币,或者将其用于进行二次支付攻击。那么他就会发现,按照规则行事、诚实工作是更有利可图的。因为该等规则使得他能够拥有更多的电子货币,而不是破坏这个系统使得其自身财富的有效性受损。 7. 回收硬盘空间 如果最近的交易已经被纳入了足够多的区块之中,那么就可以丢弃该交易之前的数据,以回收硬盘空间。为了同时确保不损害区块的随机散列值,交易信息被随机散列时,被构建成一种Merkle树(Merkle tree)7的形态,使得只有根(root)被纳入了区块的随机散列值。通过将该树(tree)的分支拔除(stubbing)的方法,老区块就能被压缩。而内部的随机散列值是不必保存的。 不含交易信息的区块头(Block header)大小仅有80字节。如果我们设定区块生成的速率为每10分钟一个,那么每一年产生的数据位4.2MB。(80 bytes * 6 * 24 * 365 = 4.2MB)。2008年,PC系统通常的内存容量为2GB,按照摩尔定律的预言,即使将全部的区块头存储于内存之中都不是问题。 8. 简化的支付确认(Simplified Payment Verification) 在不运行完整网络节点的情况下,也能够对支付进行检验。一个用户需要保留最长的工作量证明链条的区块头的拷贝,它可以不断向网络发起询问,直到它确信自己拥有最长的链条,并能够通过merkle的分支通向它被加上时间戳并纳入区块的那次交易。节点想要自行检验该交易的有效性原本是不可能的,但通过追溯到链条的某个位置,它就能看到某个节点曾经接受过它,并且于其后追加的区块也进一步证明全网曾经接受了它。 当此情形,只要诚实的节点控制了网络,检验机制就是可靠的。但是,当全网被一个计算力占优的攻击者攻击时,将变得较为脆弱。因为网络节点能够自行确认交易的有效性,只要攻击者能够持续地保持计算力优势,简化的机制会被攻击者焊接的(fabricated)交易欺骗。那么一个可行的策略就是,只要他们发现了一个无效的区块,就立刻发出警报,收到警报的用户将立刻开始下载被警告有问题的区块或交易的完整信息,以便对信息的不一致进行判定。对于日常会发生大量收付的商业机构,可能仍会希望运行他们自己的完整节点,以保持较大的独立完全性和检验的快速性。 9. 价值的组合与分割(Combining and Splitting Value) 虽然可以单个单个地对电子货币进行处理,但是对于每一枚电子货币单独发起一次交易将是一种笨拙的办法。为了使得价值易于组合与分割,交易被设计为可以纳入多个输入和输出。一般而言是某次价值较大的前次交易构成的单一输入,或者由某几个价值较小的前次交易共同构成的并行输入,但是输出最多只有两个:一个用于支付,另一个用于找零(如有)。 需要指出的是,虽然一笔交易依赖于之前的多笔交易、这些交易又各自依赖于多笔交易,但是这并不存在任何问题。因为这个工作机制并不需要展开检验之前发生的所有交易历史。 10. 隐私(Privacy) 传统的造币厂模型为交易的参与者提供了一定程度的隐私保护,因为试图向可信任的第三方索取交易信息是严格受限的。但是如果将交易信息向全网进行广播,就意味着这样的方法失效了。但是隐私依然可以得到保护:将公钥保持为匿名。公众得知的信息仅仅是有某个人将一定数量的货币发所给了另外一个人,但是难以将该交易同某个特定的人联系在一起,也就是说,公众难以确信,这些人究竟是谁。这同股票交易所发布的信息是类似的,每一手股票买卖发生的时间、交易量是记录在案且可供查询的,但是交易双方的身份信息却不予透露。 作为额外的预防措施,使用者可以让每次交易都生成一个新的地址,以确保这些交易不被追溯到一个共同的所有者。不过由于存在并行输入,一定程度上的追溯还是不可避免的,因为并行输入暗示这些货币都属于同一个所有者。此时的风险在于,如果某个人的某一个公钥被确认属于他,那么就可以追溯出此人的其它很多交易。 11. 计算 设想如下场景:一个攻击者试图比诚实节点产生链条更快地制造替代性区块链。即便它达到了这一目的,但是整个系统也并非就此完全受制于攻击者的独断意志了,比方说凭空创造价值,或者掠夺本不属于攻击者的货币。这是因为节点将不会接受无效的交易,而诚实的节点永远不会接受一个包含了无效信息的区块。一个攻击者能做的,最多是更改他自己的交易信息,并试图拿回他刚刚付给别人的钱。 诚实链条和攻击者链条之间的竞赛,可以用二叉树随机漫步(Binomial Random Walk)来描述。成功事件定义为诚实链条延长了一个区块,使其领先性+1,而失败事件则是攻击者的链条被延长了一个区块,使得差距-1。 攻击者成功填补某一既定差距的可能性,可以近似地看做赌徒破产问题(Gambler’s Ruin problem)。假定一个赌徒拥有无限的透支信用,然后开始进行潜在次数为无穷的赌博,试图填补上自己的亏空。那么我们可以计算他填补上亏空的概率,也就是该攻击者赶上诚实链条,如下所示8: 其中, p:诚实节点制造出下一个节点的概率 q:攻击者制造出下一个节点的概率 qz:攻击者最终消弭了z个区块的落后差距 假定p>q,那么攻击成功的概率就因为区块数的增长而呈现指数化下降。由于概率是攻击者的敌人,如果他不能幸运且快速地获得成功,那么他获得成功的机会随着时间的流逝就变得愈发渺茫。那么我们考虑一个收款人需要等待多长时间,才能足够确信付款人已经难以更改交易了。我们假设付款人是一个支付攻击者,希望让收款人在一段时间内相信他已经付过款了,然后立即将支付的款项重新支付给自己。虽然收款人届时会发现这一点,但为时已晚。 收款人生成了新的一对密钥组合,然后只预留一个较短的时间将公钥发送给付款人。这将可以防止以下情况:付款人预先准备好一个区块链然后持续地对此区块进行运算,直到运气让他的区块链超越了诚实链条,方才立即执行支付。当此情形,只要交易一旦发出,攻击者就开始秘密地准备一条包含了该交易替代版本的平行链条。 然后收款人将等待交易出现在首个区块中,然后在等到z个区块链接其后。此时,他仍然不能确切知道攻击者已经进展了多少个区块,但是假设诚实区块将耗费平均预期时间以产生一个区块,那么攻击者的潜在进展就是一个泊松分布,分布的期望值为: λ=z(q/p) 当此情形,为了计算攻击者追赶上的概率,我们将攻击者取得进展区块数量的泊松分布的概率密度,乘以在该数量下攻击者依然能够追赶上的概率。 为避免对无限数列求和,化为如下形式: 写为如下C语言代码: #include <math.h> double AttackerSuccessProbability(double q, int z) { double p = 1.0 - q; double lambda = z * (q / p); double sum = 1.0; int i, k; for (k = 0; k <= z; k++) { double poisson = exp(-lambda); for (i = 1; i <= k; i++) poisson *= lambda / i; sum -= poisson * (1 - pow(q / p, z - k)); } return sum; } 对其进行运算,我们可以得到如下的概率结果,发现概率对z值呈指数下降。 当q=0.1时 z=0 P=1.0000000 z=1 P=0.2045873 z=2 P=0.0509779 z=3 P=0.0131722 z=4 P=0.0034552 z=5 P=0.0009137 z=6 P=0.0002428 z=7 P=0.0000647 z=8 P=0.0000173 z=9 P=0.0000046 z=10 P=0.0000012 当q=0.3时 z=0 P=1.0000000 z=5 P=0.1773523 z=10 P=0.0416605 z=15 P=0.0101008 z=20 P=0.0024804 z=25 P=0.0006132 z=30 P=0.0001522 z=35 P=0.0000379 z=40 P=0.0000095 z=45 P=0.0000024 z=50 P=0.0000006 求解令P<0.1%的z值: 为使P<0.001,则 q=0.10 z=5 q=0.15 z=8 q=0.20 z=11 q=0.25 z=15 q=0.30 z=24 q=0.35 z=41 q=0.40 z=89 q=0.45 z=340 12.结论 我们在此提出了一种不需要信用中介的电子支付系统。我们首先讨论了通常的电子货币的电子签名原理,虽然这种系统为所有权提供了强有力的控制,但是不足以防止双重支付。为了解决这个问题,我们提出了一种采用工作量证明机制的点对点网络来记录交易的公开信息,只要诚实的节点能够控制绝大多数的CPU计算能力,就能使得攻击者事实上难以改变交易记录。该网络的强健之处在于它结构上的简洁性。节点之间的工作大部分是彼此独立的,只需要很少的协同。每个节点都不需要明确自己的身份,由于交易信息的流动路径并无任何要求,所以只需要尽其最大努力传播即可。节点可以随时离开网络,而想重新加入网络也非常容易,因为只需要补充接收离开期间的工作量证明链条即可。节点通过自己的CPU计算力进行投票,表决他们对有效区块的确认,他们不断延长有效的区块链来表达自己的确认,并拒绝在无效的区块之后延长区块以表示拒绝。 本框架包含了一个P2P电子货币系统所需要的全部规则和激励措施。 参考文献 [1] W Dai(戴伟),a scheme for a group of untraceable digital pseudonyms to pay each other with money and to enforce contracts amongst themselves without outside help(一种能够借助电子假名在群体内部相互支付并迫使个体遵守规则且不需要外界协助的电子现金机制), “B-money”, http://www.weidai.com/bmoney.txt , 1998 [2] H. Massias, X.S. Avila, and J.-J. Quisquater, "Design of a secure timestamping service with minimal trust requirements,"(在最小化信任的基础上设计一种时间戳服务器) In 20th Symposium on Information Theory in the Benelux, May 1999. [3] S. Haber, W.S. Stornetta, "How to time-stamp a digital document," (怎样为电子文件添加时间戳)In Journal of Cryptology, vol 3, No.2, pages 99-111, 1991. [4] D. Bayer, S. Haber, W.S. Stornetta, "Improving the efficiency and reliability of digital time-stamping,"(提升电子时间戳的效率和可靠性) In Sequences II: Methods in Communication, Security and Computer Science, pages 329-334, 1993. [5] S. Haber, W.S. Stornetta, "Secure names for bit-strings,"(比特字串的安全命名) In Proceedings of the 4th ACM Conference on Computer and Communications Security, pages 28-35, April 1997. on Computer and Communications Security, pages 28-35, April,1997. [6] A. Back, "Hashcash - a denial of service counter-measure,"(哈希现金——拒绝服务式攻击的克制方法) http://www.hashcash.org/papers/hashcash.pdf , 2002. [7] R.C. Merkle, "Protocols for public key cryptosystems," (公钥密码系统的协议)In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980. [8] W. Feller, "An introduction to probability theory and its applications," (概率学理论与应用导论)1957 原文 bitcoin.pdf 180k

Bitcoin: A Peer-to-Peer Electronic Cash System 中本聪(Satoshi Nakamoto) 原文: www.bitcoin.org ,翻译: www.8btc.com [摘要]:本文提出了一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名(Digital signatures)部分解决了这个问题,但是如果仍然需要第三方的支持才能防止双重支付(double-spending)的话,那么这种系统也就失去了存在的价值。我们(we)在此提出一种解决方案,使现金系统在点对点的环境下运行,并防止双重支付问题。该网络通过随机散列(hashing)对全部交易加上时间戳(timestamps),将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列(sequence)的证明,而且被看做是来自CPU计算能力最大的池(pool)。只要大多数的CPU计算能力都没有打算合作起来对全网进行攻击,那么诚实的节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点(nodes)可以随时离开和重新加入网络,并将最长的工作量证明链条作为在该节点离线期间发生的交易的证明。 1. 引言 互联网上的贸易,几乎都需要借助金融机构作为可资信赖的第三方来处理电子支付信息。虽然在绝大多数情况下这类系统都运作良好,但是这类系统仍然内生性地受制于“基于信用的模式”(trust based model)的弱点。人们无法实现完全不可逆的交易,因为金融机构总是不可避免地会出面协调争端。而金融中介的存在,也会增加交易的成本,并且限制了实际可行的最小交易规模,也限制了日常的小额支付交易。并且潜在的损失还在于,很多商品和服务本身是无法退货的,如果缺乏不可逆的支付手段,互联网的贸易就大大受限。因为有潜在的退款的可能,就需要交易双方拥有信任。此外,因为商家也必须对自己的客户小心提防,所以会向客户索取完全不必要的个人信息。而实际的商业行为中,一定比例的欺诈性客户也被认为是不可避免的,相关损失视作销售费用处理。而在使用物理现金的情况下,因为此时没有第三方信用中介的存在,这些销售费用和支付问题上的不确定性却是可以避免的。 所以,我们非常需要这样一种电子支付系统,它基于密码学原理而不基于信用,使得任何达成一致的双方,能够直接进行支付,从而不需要第三方中介的参与。杜绝回滚(reverse)支付交易的可能,这就可以保护特定的卖家免于欺诈;而对于想要保护买家的人来说,在此环境下设立通常的第三方担保机制也可谓轻松加愉快。在这篇论文中,我们(we)将提出一种通过点对点分布式的时间戳服务器来生成依照时间前后排列并加以记录的电子交易证明,从而解决双重支付问题。只要诚实的节点所控制的计算能力的总和,大于有合作关系的(cooperating)攻击者的计算能力的总和,该系统就是安全的。 2. 交易(Transactions) 我们定义,一枚电子货币(an electronic coin)是这样的一串数字签名:每一位所有者通过对前一次交易和下一位拥有者的公钥(Public key) 签署一个随机散列的数字签名,并将这个签名附加在这枚电子货币的末尾,电子货币就发送给了下一位所有者。而收款人通过对签名进行检验,就能够验证该链条的所有者。 (附件:279414) 该过程的问题在于,收款人将难以检验,之前的某位所有者,是否对这枚电子货币进行了双重支付。通常的解决方案,就是引入信得过的第三方权威,或者类似于造币厂(mint)的机构,来对每一笔交易进行检验,以防止双重支付。在每一笔交易结束后,这枚电子货币就要被造币厂回收,而造币厂将发行一枚新的电子货币;而只有造币厂直接发行的电子货币,才算作有效,这样就能够防止双重支付。可是该解决方案的问题在于,整个货币系统的命运完全依赖于运作造币厂的公司,因为每一笔交易都要经过该造币厂的确认,而该造币厂就好比是一家银行。 我们需要收款人有某种方法,能够确保之前的所有者没有对更早发生的交易实施签名。从逻辑上看,为了达到目的,实际上我们需要关注的只是于本交易之前发生的交易,而不需要关注这笔交易发生之后是否会有双重支付的尝试。为了确保某一次交易是不存在的,那么唯一的方法就是获悉之前发生过的所有交易。在造币厂模型里面,造币厂获悉所有的交易,并且决定了交易完成的先后顺序。如果想要在电子系统中排除第三方中介机构,那么交易信息就应当被公开宣布(publicly announced)1,我们需要整个系统内的所有参与者,都有唯一公认的历史交易序列。收款人需要确保在交易期间绝大多数的节点都认同该交易是首次出现。 3. 时间戳服务器(Timestamp server) 本解决方案首先提出一个“时间戳服务器”。时间戳服务器通过对以区块(block)形式存在的一组数据实施随机散列而加上时间戳,并将该随机散列进行广播,就像在新闻或世界性新闻组网络(Usenet)的发帖一样2345。显然,该时间戳能够证实特定数据必然于某特定时刻是的确存在的,因为只有在该时刻存在了才能获取相应的随机散列值。每个时间戳应当将前一个时间戳纳入其随机散列值中,每一个随后的时间戳都对之前的一个时间戳进行增强(reinforcing),这样就形成了一个链条(Chain)。 (附件:279415) 4. 工作量证明(Proof-of-Work) 为了在点对点的基础上构建一组分散化的时间戳服务器,仅仅像报纸或世界性新闻网络组一样工作是不够的,我们还需要一个类似于亚当·柏克(Adam Back)提出的哈希现金(Hashcash)6。在进行随机散列运算时,工作量证明机制引入了对某一个特定值的扫描工作,比方说SHA-256下,随机散列值以一个或多个0开始。那么随着0的数目的上升, 找到这个解所需要的工作量将呈指数增长,但是检验结果仅需要一次随机散列运算。 我们在区块中补增一个随机数(Nonce),这个随机数要使得该给定区块的随机散列值出现了所需的那么多个0。我们通过反复尝试来找到这个随机数,找到为止。这样我们就构建了一个工作量证明机制。只要该CPU耗费的工作量能够满足该工作量证明机制,那么除非重新完成相当的工作量,该区块的信息就不可更改。由于之后的区块是链接在该区块之后的,所以想要更改该区块中的信息,就还需要重新完成之后所有区块的全部工作量。 (附件:279416) 同时,该工作量证明机制还解决了在集体投票表决时,谁是大多数的问题。如果决定大多数的方式是基于IP地址的,一IP地址一票,那么如果有人拥有分配大量IP地址的权力,则该机制就被破坏了。而工作量证明机制的本质则是一CPU一票。“大多数”的决定表达为最长的链,因为最长的链包含了最大的工作量。如果大多数的CPU为诚实的节点控制,那么诚实的链条将以最快的速度延长,并超越其他的竞争链条。如果想要对业已出现的区块进行修改,攻击者必须重新完成该区块的工作量外加该区块之后所有区块的工作量,并最终赶上和超越诚实节点的工作量。我们将在后文证明,设想一个较慢的攻击者试图赶上随后的区块,那么其成功概率将呈指数化递减。 另一个问题是,硬件的运算速度在高速增长,且节点参与网络的程度会有所起伏。为了解决这个问题,工作量证明的难度(the proof-of-work difficulty)将采用移动平均目标的方法来确定,即令难度指向令每小时生成区块的速度为某一预设的平均数。如果区块生成的速度过快,那么难度就会提高。 5. 网络 运行该网络的步骤如下: 1) 新的交易向全网进行广播; 2) 每一个节点都将收到的交易信息纳入一个区块中; 3) 每个节点都尝试在自己的区块中找到一个具有足够难度的工作量证明; 4) 当一个节点找到了一个工作量证明,它就向全网进行广播; 5) 当且仅当包含在该区块中的所有交易都是有效的且之前未存在过的,其他节点才认同该区块的有效性; 6) 其他节点表示他们接受该区块,而表示接受的方法,则是在跟随该区块的末尾,制造新的区块以延长该链条,而将被接受区块的随机散列值视为先于新区快的随机散列值。 节点始终都将最长的链条视为正确的链条,并持续工作和延长它。如果有两个节点同时广播不同版本的新区块,那么其他节点在接收到该区块的时间上将存在先后差别。当此情形,他们将在率先收到的区块基础上进行工作,但也会保留另外一个链条,以防后者变成最长的链条。该僵局(tie)的打破要等到下一个工作量证明被发现,而其中的一条链条被证实为是较长的一条,那么在另一条分支链条上工作的节点将转换阵营,开始在较长的链条上工作。 所谓“新的交易要广播”,实际上不需要抵达全部的节点。只要交易信息能够抵达足够多的节点,那么他们将很快被整合进一个区块中。而区块的广播对被丢弃的信息是具有容错能力的。如果一个节点没有收到某特定区块,那么该节点将会发现自己缺失了某个区块,也就可以提出自己下载该区块的请求。 6. 激励 我们约定如此:每个区块的第一笔交易进行特殊化处理,该交易产生一枚由该区块创造者拥有的新的电子货币。这样就增加了节点支持该网络的激励,并在没有中央集权机构发行货币的情况下,提供了一种将电子货币分配到流通领域的一种方法。这种将一定数量新货币持续增添到货币系统中的方法,非常类似于耗费资源去挖掘金矿并将黄金注入到流通领域。此时,CPU的时间和电力消耗就是消耗的资源。 另外一个激励的来源则是交易费(transaction fees)。如果某笔交易的输出值小于输入值,那么差额就是交易费,该交易费将被增加到该区块的激励中。只要既定数量的电子货币已经进入流通,那么激励机制就可以逐渐转换为完全依靠交易费,那么本货币系统就能够免于通货膨胀。 激励系统也有助于鼓励节点保持诚实。如果有一个贪婪的攻击者能够调集比所有诚实节点加起来还要多的CPU计算力,那么他就面临一个选择:要么将其用于诚实工作产生新的电子货币,或者将其用于进行二次支付攻击。那么他就会发现,按照规则行事、诚实工作是更有利可图的。因为该等规则使得他能够拥有更多的电子货币,而不是破坏这个系统使得其自身财富的有效性受损。 7. 回收硬盘空间 如果最近的交易已经被纳入了足够多的区块之中,那么就可以丢弃该交易之前的数据,以回收硬盘空间。为了同时确保不损害区块的随机散列值,交易信息被随机散列时,被构建成一种Merkle树(Merkle tree)7的形态,使得只有根(root)被纳入了区块的随机散列值。通过将该树(tree)的分支拔除(stubbing)的方法,老区块就能被压缩。而内部的随机散列值是不必保存的。 (附件:279417) 不含交易信息的区块头(Block header)大小仅有80字节。如果我们设定区块生成的速率为每10分钟一个,那么每一年产生的数据位4.2MB。(80 bytes * 6 * 24 * 365 = 4.2MB)。2008年,PC系统通常的内存容量为2GB,按照摩尔定律的预言,即使将全部的区块头存储于内存之中都不是问题。 8. 简化的支付确认(Simplified Payment Verification) 在不运行完整网络节点的情况下,也能够对支付进行检验。一个用户需要保留最长的工作量证明链条的区块头的拷贝,它可以不断向网络发起询问,直到它确信自己拥有最长的链条,并能够通过merkle的分支通向它被加上时间戳并纳入区块的那次交易。节点想要自行检验该交易的有效性原本是不可能的,但通过追溯到链条的某个位置,它就能看到某个节点曾经接受过它,并且于其后追加的区块也进一步证明全网曾经接受了它。 (附件:279418) 当此情形,只要诚实的节点控制了网络,检验机制就是可靠的。但是,当全网被一个计算力占优的攻击者攻击时,将变得较为脆弱。因为网络节点能够自行确认交易的有效性,只要攻击者能够持续地保持计算力优势,简化的机制会被攻击者焊接的(fabricated)交易欺骗。那么一个可行的策略就是,只要他们发现了一个无效的区块,就立刻发出警报,收到警报的用户将立刻开始下载被警告有问题的区块或交易的完整信息,以便对信息的不一致进行判定。对于日常会发生大量收付的商业机构,可能仍会希望运行他们自己的完整节点,以保持较大的独立完全性和检验的快速性。 (附件:279419) 9. 价值的组合与分割(Combining and Splitting Value) 虽然可以单个单个地对电子货币进行处理,但是对于每一枚电子货币单独发起一次交易将是一种笨拙的办法。为了使得价值易于组合与分割,交易被设计为可以纳入多个输入和输出。一般而言是某次价值较大的前次交易构成的单一输入,或者由某几个价值较小的前次交易共同构成的并行输入,但是输出最多只有两个:一个用于支付,另一个用于找零(如有)。 需要指出的是,虽然一笔交易依赖于之前的多笔交易、这些交易又各自依赖于多笔交易,但是这并不存在任何问题。因为这个工作机制并不需要展开检验之前发生的所有交易历史。 10. 隐私(Privacy) (附件:279420) 传统的造币厂模型为交易的参与者提供了一定程度的隐私保护,因为试图向可信任的第三方索取交易信息是严格受限的。但是如果将交易信息向全网进行广播,就意味着这样的方法失效了。但是隐私依然可以得到保护:将公钥保持为匿名。公众得知的信息仅仅是有某个人将一定数量的货币发所给了另外一个人,但是难以将该交易同某个特定的人联系在一起,也就是说,公众难以确信,这些人究竟是谁。这同股票交易所发布的信息是类似的,每一手股票买卖发生的时间、交易量是记录在案且可供查询的,但是交易双方的身份信息却不予透露。 作为额外的预防措施,使用者可以让每次交易都生成一个新的地址,以确保这些交易不被追溯到一个共同的所有者。不过由于存在并行输入,一定程度上的追溯还是不可避免的,因为并行输入暗示这些货币都属于同一个所有者。此时的风险在于,如果某个人的某一个公钥被确认属于他,那么就可以追溯出此人的其它很多交易。 11. 计算 设想如下场景:一个攻击者试图比诚实节点产生链条更快地制造替代性区块链。即便它达到了这一目的,但是整个系统也并非就此完全受制于攻击者的独断意志了,比方说凭空创造价值,或者掠夺本不属于攻击者的货币。这是因为节点将不会接受无效的交易,而诚实的节点永远不会接受一个包含了无效信息的区块。一个攻击者能做的,最多是更改他自己的交易信息,并试图拿回他刚刚付给别人的钱。 诚实链条和攻击者链条之间的竞赛,可以用二叉树随机漫步(Binomial Random Walk)来描述。成功事件定义为诚实链条延长了一个区块,使其领先性+1,而失败事件则是攻击者的链条被延长了一个区块,使得差距-1。 攻击者成功填补某一既定差距的可能性,可以近似地看做赌徒破产问题(Gambler’s Ruin problem)。假定一个赌徒拥有无限的透支信用,然后开始进行潜在次数为无穷的赌博,试图填补上自己的亏空。那么我们可以计算他填补上亏空的概率,也就是该攻击者赶上诚实链条,如下所示8: (附件:279421) 其中, p:诚实节点制造出下一个节点的概率 q:攻击者制造出下一个节点的概率 qz:攻击者最终消弭了z个区块的落后差距 假定p>q,那么攻击成功的概率就因为区块数的增长而呈现指数化下降。由于概率是攻击者的敌人,如果他不能幸运且快速地获得成功,那么他获得成功的机会随着时间的流逝就变得愈发渺茫。那么我们考虑一个收款人需要等待多长时间,才能足够确信付款人已经难以更改交易了。我们假设付款人是一个支付攻击者,希望让收款人在一段时间内相信他已经付过款了,然后立即将支付的款项重新支付给自己。虽然收款人届时会发现这一点,但为时已晚。 收款人生成了新的一对密钥组合,然后只预留一个较短的时间将公钥发送给付款人。这将可以防止以下情况:付款人预先准备好一个区块链然后持续地对此区块进行运算,直到运气让他的区块链超越了诚实链条,方才立即执行支付。当此情形,只要交易一旦发出,攻击者就开始秘密地准备一条包含了该交易替代版本的平行链条。 然后收款人将等待交易出现在首个区块中,然后在等到z个区块链接其后。此时,他仍然不能确切知道攻击者已经进展了多少个区块,但是假设诚实区块将耗费平均预期时间以产生一个区块,那么攻击者的潜在进展就是一个泊松分布,分布的期望值为: λ=z(q/p) 当此情形,为了计算攻击者追赶上的概率,我们将攻击者取得进展区块数量的泊松分布的概率密度,乘以在该数量下攻击者依然能够追赶上的概率。 (附件:279422) 为避免对无限数列求和,化为如下形式: (附件:279423) 写为如下C语言代码: #include <math.h> double AttackerSuccessProbability(double q, int z) { double p = 1.0 - q; double lambda = z * (q / p); double sum = 1.0; int i, k; for (k = 0; k <= z; k++) { double poisson = exp(-lambda); for (i = 1; i <= k; i++) poisson *= lambda / i; sum -= poisson * (1 - pow(q / p, z - k)); } return sum; } 对其进行运算,我们可以得到如下的概率结果,发现概率对z值呈指数下降。 当q=0.1时 z=0 P=1.0000000 z=1 P=0.2045873 z=2 P=0.0509779 z=3 P=0.0131722 z=4 P=0.0034552 z=5 P=0.0009137 z=6 P=0.0002428 z=7 P=0.0000647 z=8 P=0.0000173 z=9 P=0.0000046 z=10 P=0.0000012 当q=0.3时 z=0 P=1.0000000 z=5 P=0.1773523 z=10 P=0.0416605 z=15 P=0.0101008 z=20 P=0.0024804 z=25 P=0.0006132 z=30 P=0.0001522 z=35 P=0.0000379 z=40 P=0.0000095 z=45 P=0.0000024 z=50 P=0.0000006 求解令P<0.1%的z值: 为使P<0.001,则 q=0.10 z=5 q=0.15 z=8 q=0.20 z=11 q=0.25 z=15 q=0.30 z=24 q=0.35 z=41 q=0.40 z=89 q=0.45 z=340 12.结论 我们在此提出了一种不需要信用中介的电子支付系统。我们首先讨论了通常的电子货币的电子签名原理,虽然这种系统为所有权提供了强有力的控制,但是不足以防止双重支付。为了解决这个问题,我们提出了一种采用工作量证明机制的点对点网络来记录交易的公开信息,只要诚实的节点能够控制绝大多数的CPU计算能力,就能使得攻击者事实上难以改变交易记录。该网络的强健之处在于它结构上的简洁性。节点之间的工作大部分是彼此独立的,只需要很少的协同。每个节点都不需要明确自己的身份,由于交易信息的流动路径并无任何要求,所以只需要尽其最大努力传播即可。节点可以随时离开网络,而想重新加入网络也非常容易,因为只需要补充接收离开期间的工作量证明链条即可。节点通过自己的CPU计算力进行投票,表决他们对有效区块的确认,他们不断延长有效的区块链来表达自己的确认,并拒绝在无效的区块之后延长区块以表示拒绝。 本框架包含了一个P2P电子货币系统所需要的全部规则和激励措施。 参考文献 [1] W Dai(戴伟),a scheme for a group of untraceable digital pseudonyms to pay each other with money and to enforce contracts amongst themselves without outside help(一种能够借助电子假名在群体内部相互支付并迫使个体遵守规则且不需要外界协助的电子现金机制), “B-money”, http://www.weidai.com/bmoney.txt , 1998 [2] H. Massias, X.S. Avila, and J.-J. Quisquater, "Design of a secure timestamping service with minimal trust requirements,"(在最小化信任的基础上设计一种时间戳服务器) In 20th Symposium on Information Theory in the Benelux, May 1999. [3] S. Haber, W.S. Stornetta, "How to time-stamp a digital document," (怎样为电子文件添加时间戳)In Journal of Cryptology, vol 3, No.2, pages 99-111, 1991. [4] D. Bayer, S. Haber, W.S. Stornetta, "Improving the efficiency and reliability of digital time-stamping,"(提升电子时间戳的效率和可靠性) In Sequences II: Methods in Communication, Security and Computer Science, pages 329-334, 1993. [5] S. Haber, W.S. Stornetta, "Secure names for bit-strings,"(比特字串的安全命名) In Proceedings of the 4th ACM Conference on Computer and Communications Security, pages 28-35, April 1997. on Computer and Communications Security, pages 28-35, April,1997. [6] A. Back, "Hashcash - a denial of service counter-measure,"(哈希现金——拒绝服务式攻击的克制方法) http://www.hashcash.org/papers/hashcash.pdf , 2002. [7] R.C. Merkle, "Protocols for public key cryptosystems," (公钥密码系统的协议)In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980. [8] W. Feller, "An introduction to probability theory and its applications," (概率学理论与应用导论)1957 原文 (附件:279424)


[机器学习笔记#4] Neural Style 算法分析及caffe实验 在之前的帖子中,楼主简要介绍了 Neural Style 算法的原理和实现思路,该方法2015年由三位德国科学家以一份tech report的形式提出,今年他们发表了一篇 CVPR 论文 。 在楼主随后的进一步实验中,发现当初好死不死选caffe是给自己挖了个大坑,caffe作为曾经占据科研界支配地位的工具,用来跑各种pre-trained的model 是相当顺手,但用来实现自定义运算的layer,尤其是用Python,其过程相当之蛋疼,而且也缺乏运算效率。于是推翻重来,改用tensorflow实现。 实现neural style的代码,首先需要一个 pre-trained VGG16 模型,tensorflow 中今年新加入的轻量级图像识别库TF-Slim提供了官方的pre-trained的VGG网络,非常方便。 VGG16 网络结构如下,其特征是大量使用两层级联的具有3x3小卷积核(能分辨上下左右概念的最小尺寸)的卷积层来代替类似Alexnet结构中的单层大卷积核卷积层,而最后的等效 receptive field 是接近的(5x5)。这样的好处一是减少了待训练weight的数量,二是小卷积核之间引入了额外的ReLu层,增加了非线性程度,使得整体的representation power提高(简单理解成用两层的MLP代替单层inear classifier)。 ['vgg_16/conv1/conv1_1', 'vgg_16/conv1/conv1_2', 'vgg_16/pool1', 'vgg_16/conv2/conv2_1', 'vgg_16/conv2/conv2_2', 'vgg_16/pool2', 'vgg_16/conv3/conv3_1', 'vgg_16/conv3/conv3_2', 'vgg_16/conv3/conv3_3', 'vgg_16/pool3', 'vgg_16/conv4/conv4_1', 'vgg_16/conv4/conv4_2', 'vgg_16/conv4/conv4_3', 'vgg_16/pool4', 'vgg_16/conv5/conv5_1', 'vgg_16/conv5/conv5_2', 'vgg_16/conv5/conv5_3', 'vgg_16/pool5', 'vgg_16/fc6', 'vgg_16/fc7', 'vgg_16/fc8'] 在实现中,原paper用conv4_2层输出的activation作为表征原图content的feature,原理是CNN中high level卷积层的neuron主要是被输入图片中物理的轮廓、形状激活,而忽略细节纹理(理解为假如我有两双同款篮球鞋,分别是不同的花纹、颜色,二者的同角度照片在该层的activation相同)。 而对于原图片的style,该paper提出使用卷积层不同filter输出的activation之间的corelation来表征,至于为什么,则出自作者的另一篇paper。这种corelation可以用inner product来计算,得到的feature称为Gram matrix。最终原图的style由 conv1_1,conv2_1,conv3_1,conv4_1,conv5_1的输出共同计算得到。 整个结构如图,出自原paper配图: 生成过程中需要提供两种图片,一张提供content,计算得到content feature;一张提供style,计算得到 style feature (gram matrix)。然后我们的目标图片会被初始化为一张target白噪声图片(随机初始化),该图输出的 content feature 和style feature 于content image 和style image的输出分别进行比较,计算其L2 distance,即得到一个L2 loss function。通过反向传播可以计算该L2 loss 在target图片像素上的gradient,然后对target图片像素进行gradient based optimization,通过迭代循环使其逐渐呈现我们想要的面貌(loss function 最小)。这里很有趣的一点是,该方法中的优化过程,并非针对CNN网络本身,反而是输入图片,虽然有些反直觉,但该方法在各种图片相关的generative model中并不少见,包括效果骇人的deepdream。


1导言 深度学习正在努力让神经网络变得越来越深 [13],因为更深的模型意味着更复杂的处理带来的更好的分类性能,诸如 [4] 等研究已经把神经网络成功地训练到了一百多层。但是我们却把目光放在让模型更宽上,因为我们认为神经网络在这一方向上还有很多的性能供我们挖掘,同时更宽的模型能够更好地并行化以利用计算能力提升带来的红利。 前人已经在这个方面做了许多的重要的工作,Ensemble-based classifiers[12] 描述的是将图像进行不同处理后并输入不同神经网络,最终输出各神经网络输出的平均的一种宽的图像分类神经网络结构,multi-column deep neural network[1] 中阐述的使用不同预处理方式处理输入数据的结构展现了卷积神经网络在这种宽结构上的优越性能。同时,本工作在一定程度上受到了 RCNN[2] 的启发,其使用神经网络对图像不同部位进行分类的方式是我们带步距卷积采样方式的雏形,而使用卷积神经网络作为卷积核的想法则来自于 network in network[11],它使用了多层神经网络替代来原本基于广义线性模型(GLM)的卷积核,带来了更优的分类性能。 我们提出的模型使用带步进的拥有大卷积核卷积层采样,使用小的卷积神经网络替代传统卷积层中的卷积核,使用全局平均池(Global Average-pooling) [11] 平均各卷积核的输出输出最终结果。小卷积神经网络于普通的用于 MNIST 数据集中的传统的卷积神经网络没有什么太大的差别,主要是使用了 Dropout[14] 技术来抑制过分的过拟合并用平均池层替代最大池层,目的是体现本模型所带来的的提升。现阶段我们已经在 MNIST数据集上完成了测试,获得了具有一定说服力的实验结果。 2模型 2.1 带步进卷积 如图 1 所示,输入图像被以带步进卷积的方式切割并输入到小卷积神经网络中,小卷积神经网络的输出结合在一起成为一个特征图,这可以看做是小卷积神经网络充当带步进卷积层的卷积核。最后本模型用全局平均池处理这个特征图输出最终分类结果。 2.2 小卷积神经网络 为了证明我们提出的方法的优势,我们只在 LeNet[8] 的基础上做了了少许改动,包括使用ReLU激活函数以加快拟合速度,增加了神经元的数量以便和现在的其他工作形成对比,在每一层后面都用了Dropout技术以抑制过拟合,把最大池换成了平均池,这些都是现在的卷积神经网络非常常用的技术。 如表 1 所示, 我们使用两种小卷积神经网络来验证我们的模型在更深的神经网络上的性能。 3实验 3.1 训练 在训练过程中,我们使用了 AdamOptimizer[5],共训练 100epoches,初始学习率设置如表 2 所示。 1 3.2MNIST 手写数字数据集 MNIST 手写数字数据集提供了六万的训练集和一万的测试集,它的图片是被规范处理过的 28*28 的灰度图。我们分别测试了我们使用的两种小卷积神经网络在我们的模型上的性能,我们实验的结果如表 3 所示,两种小卷积神经网络的测试结果分别为 0.37和 0.33,可以看出,我们的模型能够为简单的卷积神经网络带来较大的提升,而且更深的小卷积神经网络也能带来更好的效果。 我们认为我们的模型在对比 RCNN-96 和 Maxout In Maxout 这样的模型的时候的时候还是有较大优势的,因为显然的,我们的模型更浅,拥有更少的参数。我们认为 Multi-column Deep Neural Networks 拥有更好的效果是因为它使用了更复杂的基分类器(卷积神经网络)(他们用了 winner-take-all 神经元等技术),我们正在考虑是否使用类似的基分类器。 4讨论 4.1 集成学习 在集成学习中,有如下公式 [7]: $$E = \overline{E} - \overline{A}$$ 其中\(E\)是模型误差,\(\overline{E}\)是各分类器平均误差,\(\overline{A}\)是各分类器的平均分歧(分类器输出与总输出的差)。 我们认为,我们提出的方法所达到的效果在一定程度上来自于我们增加了各分类器的分歧,虽然我们在一定程度上减小了各分类器的分类精度,但平均分歧的增加是大于误差的增加的。 4.2 过拟合 按照[11] 的说法,由于各个小神经网络之间的权值存在共享,所以它们难以形成强 的组合,从而减少了过拟合的可能,但是在我们的模型中却存在难以估量的过拟合,以 至于我们几乎在每一个层中都用了 dropout 来减小过拟合程度(甚至包括输出层,当然, 我们认为在防止过拟合的诸多措施中,输出层的 dropout 在防止过拟合方面有不容小觑 的贡献)。我们认为这些过拟合的原因是各个卷积核对图像上的同一个区域重复采样了 过多次,我们现在还在致力于解决这一问题:一方面,更小的卷积核会使得小卷积神经 网络的输入图像过小,这让小卷积神经网络更难收敛;另一方面,小神经网络内必须使 用卷积来实现更好的图像能力。 4.3 学习率 这样的学习率是极其夸张的,按照道理来说,这样会严重过拟合(实验结果确实如 此),但是事实上只有这样的学习率才能成功地把模型训练出来,所以我们在输出层加 Dropout,以平衡大学习率带来的影响。一些关于 Batch normalization(BN)的实验显 示,在用了 BN 之后,在使用大学习率时收敛速度很快,似乎可以使用正常的学习率进 行训练,但是这时还是难以训练到模型在之前所能达到的效果。 4.4 更多 CNNIC 层 在我们的早期的尝试中,我们曾经尝试使用多个 CNNIC 层来进行分类,但是我们 面临着许多问题:a) 为了使用更多的 CNNIC 层,我们不得不使用为 1 的步进或者较小 步进,这样带来的是计算量上的爆炸式的增长,对于模型来讲是灾难性的。b) 如我们之 前的实验所展现的,使用步进为 1 或者较小步进的 CNNIC 层无法带来任何提升,只有 计算量的增长。 4.5 超参数 这样的一个结构有许许多多的超参数,诸如小卷积神经网络的输入的图像的尺寸、 卷积层步进大小、Dropout 率、学习率和,很多的参数的调节是没有先例可循的,其中 固然存在一些的超参数的调节能够体现模型本身的特性,但是我们并不想把过多的时间 花在这些参数的调节上。 5下一步工作 更多改进 我们正在尝试使用 [10] 中所采用的 Batch normalization 来提升我们的模型 的性能,同时更多的 tricky (雾)也在考虑之中。 更多的数据集上的实验 CIFAR-10/100[6] 是彩色图像数据集,我们将在这个数据集上 进行进一步的实验,以进一步证明模型的优势。 特征图可视化 [11] 中展示了其特征图并对其进行了分析,我们认为这样做有利于人们 理解这一结构。 参考文献 [1] Dan Ciregan, Ueli Meier, and Jurgen Schmidhuber. Multi-column deep neural networks for image classification. computer vision and pattern recognition, pages 3642–3649, 2012. [2] Ross Girshick, Jeff Donahue, Trevor Darrell, and Jitendra Malik. Rich feature hierarchies for accurate object detection and semantic segmentation. pages 580– 587, 2013. [3] Ian J Goodfellow, David Warde-Farley, Mehdi Mirza, Aaron Courville, and Yoshua Bengio. Maxout networks. Computer Science, pages 1319–1327, 2013. [4] Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. Deep residual learning for image recognition. computer vision and pattern recognition, pages 770–778, 2016. [5] Diederik P Kingma and Jimmy Ba. Adam: A method for stochastic optimization. Computer Science, 2014. [6] Alex Krizhevsky. Learning multiple layers of features from tiny images. 2009. [7] Anders Krogh and Jesper Vedelsby. Neural network ensembles, cross validation, and active learning. In NIPS, 1994. [8] Yann Lecun, Leon Bottou, Yoshua Bengio, and Patrick Haffner. Gradient-based learning applied to document recognition. Proceedings of the IEEE, 86(11):2278– 2324, 1998. [9] Ming Liang and Xiaolin Hu. Recurrent convolutional neural network for object recognition. pages 3367–3375, 2015. [10] Zhibin Liao and Gustavo Carneiro. On the importance of normalisation layers in deep learning with piecewise linear activation units. workshop on applications of computer vision, pages 1–8, 2016. [11] Min Lin, Qiang Chen, and Shuicheng Yan. Network in network. international conference on learning representations, 2014. [12] Lior Rokach. Ensemble-based classifiers. Artificial Intelligence Review, 33:1–39,2010. [13] Karen Simonyan and Andrew Zisserman. Very deep convolutional networks for large-scale image recognition. international conference on learning representations,2015. [14] Nitish Srivastava, Geoffrey E Hinton, Alex Krizhevsky, Ilya Sutskever, and Ruslan Salakhutdinov. Dropout: a simple way to prevent neural networks from overfitting. Journal of Machine Learning Research, 15(1):1929–1958, 2014. A 实现 在这个链接中 2 你可以找到本模型的相关代码。 1:训练在由科创基金资助的 NVIDIA GTX1060 6GB 上进行 2: https://github.com/MyWorkShop/Convolutional-Neural-Networks-in-Convolution 本报告PDF版本下载: mid_report.pdf 216k 3次


我从16年10月开始接触机器学习。以独立研究工作者的身份,实验从早做到晚。17年10月份又参加了个RL比赛(具体搜Learning to Run),忙得昏天黑地。 AI是一个发展飞快的领域。 做前沿研究,辛苦且不论,投入产出比低得吓人。 图像特征提取+识别,目前的主流方法是CNN,CNN像积木一样,可以以无穷多的方式组合成一个完整的神经网络,但是只有某些特定的组合方式,在经过训练后能达到较好的性能(比如较高的识别准确度等等)。 连续5年,学界每年都会用新的神经网络架构,刷新若干次图像识别准确率的记录。 图源《2017AI报告》(推荐阅读): http://cdn.aiindex.org/2017-report.pdf 正当大家都以为由He Kaiming提出的ResNet架构已经达到了前人难以企及的高准确率和高参数效率(同样性能下使用更少的自由参数)的时候,WideResNet Xception等架构又接二连三地跳出来。 不过进度虽然飞快,但是对于相关原理的解释(“为什么这样的网络能达到这样的效果”)非常少。正如12月NIPS会议上,与会者提出的那样:目前的神经网络研究给人以炼金术的感觉。 这就有点像医学研究,由于人体实在是太复杂,即便有了现代生物技术的辅助,对于疑难杂症往往还是用统计学方法总结出有效的方案,没有办法像物理学那样提出统一、普遍适用的理论。 不管从个人的经验,还是领域内发展的结果来看,如果想在这些研究方向上做出任何突破,第一步就是要先追赶上其他人的研究(把人家做出来的效果重复一次)。斯坦福的学生用着由Nvidia捐赠的机器,能够比你快100倍地重复出其他人的研究成果。对于捉襟见肘的独立研究者而言,超过SOTA(业界第一)的水平几乎是零概率事件。即便拿下SOTA,风光也不会超过3个月。 好在这个领域的论文都是公开发表且免费传播的——即便第一时间你无法马上重复SOTA的实验,你也仍然可以掌握它的“方法论”和“世界观”,用到日后的工作中去。从这个角度来看,人工智能/机器学习领域的研究其实是目前所有学科里效率最高、进度最快、成果最透明的。 最初选择在这上面花时间,纯粹是因为想掌握各种SOTA酷炫吊炸的技能;一路走来,受益良多,期间把CS本科的课程补了个遍——若非为此,很多教材可能这辈子再不会碰。 对所谓“无知无畏”,有了深层次的体会。最开始学Machine Learning的时候,想法是这东西真有用,原理也简单,那么多人感兴趣想入门,完全应该写教程,写书单,写这个那个,为乡亲们造福……后来随着时间流逝,实验做得越来越多,接触的领域越来越多,才意识到连开给自己的书单都没时间看/看不下去,更别说写什么教程文章了,倒不是怕误人子弟,而是知道一定会误人子弟。有道是,写书的不做研究,做研究的不写书——这句话必须先做了研究才能理解呀。 题外话:我将会参加一月份的AMLD conference。 在茶话读到一篇喷Elsevier的文章,原版之前看过了。学术出版曾经是很专业的工作,手写的论文,出版商要转成铅字,包括数学公式也要帮你排好, 而且还不能犯错(意味着干活的人必须能看懂文章意思) ,到最后印刷成书,人力成本是很高的。 现在很多论文是由作者排版的(LaTeX),出版商的作用就削减了,包括现在还可以下载论文,连纸张都免了。所以大家就抗议价格太贵。 其实可以从政治的角度来看:学术论文是记载知识的,可知识的定义如此广泛,暂时没有用途的知识,将来可能会有价值。所以如果有一本万国期刊,允许所有人自由发表论文,后果就是上面会出现大量类似“吃什么可以祛痘”之类的文章。大家为啥不把文章发到那上面去呢?答案很简单:虽然你可以分辨论文的优劣,但当局分辨不了啊。 学术研究是要消耗经费的,因此提供经费的当局(通常是政府)必然要通过一定的标准来决定是否对一项研究提供经费。如果这个标准选得不好,就会造成经费的浪费。所以学术会议还是得开,论文集还是得出——作为学术界自身通过民主方式(审稿)决定谁能获得经费的一种制度。如果没有这个制度,大家都拿不到经费。大家给学术出版商支付的下载费用,可以视为一种税款,用于维持整个制度的运转。作为这个制度的运作载体,也就是期刊们——也就获得了巨大的政治力量,以至于收钱收得有些难看了(毕竟政府的反射弧长度是20光年)。

我从16年10月开始接触机器学习。以独立研究工作者的身份,实验从早做到晚。17年10月份又参加了个RL比赛(具体搜Learning to Run),忙得昏天黑地。 AI是一个发展飞快的领域。 (附件:279337) 做前沿研究,辛苦且不论,投入产出比低得吓人。 图像特征提取+识别,目前的主流方法是CNN,CNN像积木一样,可以以无穷多的方式组合成一个完整的神经网络,但是只有某些特定的组合方式,在经过训练后能达到较好的性能(比如较高的识别准确度等等)。 连续5年,学界每年都会用新的神经网络架构,刷新若干次图像识别准确率的记录。 (附件:279338) 图源《2017AI报告》(推荐阅读): http://cdn.aiindex.org/2017-report.pdf 正当大家都以为由He Kaiming提出的ResNet架构已经达到了前人难以企及的高准确率和高参数效率(同样性能下使用更少的自由参数)的时候,WideResNet Xception等架构又接二连三地跳出来。 不过进度虽然飞快,但是对于相关原理的解释(“为什么这样的网络能达到这样的效果”)非常少。正如12月NIPS会议上,与会者提出的那样:目前的神经网络研究给人以炼金术的感觉。 这就有点像医学研究,由于人体实在是太复杂,即便有了现代生物技术的辅助,对于疑难杂症往往还是用统计学方法总结出有效的方案,没有办法像物理学那样提出统一、普遍适用的理论。 不管从个人的经验,还是领域内发展的结果来看,如果想在这些研究方向上做出任何突破,第一步就是要先追赶上其他人的研究(把人家做出来的效果重复一次)。斯坦福的学生用着由Nvidia捐赠的机器,能够比你快100倍地重复出其他人的研究成果。对于捉襟见肘的独立研究者而言,超过SOTA(业界第一)的水平几乎是零概率事件。即便拿下SOTA,风光也不会超过3个月。 好在这个领域的论文都是公开发表且免费传播的——即便第一时间你无法马上重复SOTA的实验,你也仍然可以掌握它的“方法论”和“世界观”,用到日后的工作中去。从这个角度来看,人工智能/机器学习领域的研究其实是目前所有学科里效率最高、进度最快、成果最透明的。 最初选择在这上面花时间,纯粹是因为想掌握各种SOTA酷炫吊炸的技能;一路走来,受益良多,期间把CS本科的课程补了个遍——若非为此,很多教材可能这辈子再不会碰。 对所谓“无知无畏”,有了深层次的体会。最开始学Machine Learning的时候,想法是这东西真有用,原理也简单,那么多人感兴趣想入门,完全应该写教程,写书单,写这个那个,为乡亲们造福……后来随着时间流逝,实验做得越来越多,接触的领域越来越多,才意识到连开给自己的书单都没时间看/看不下去,更别说写什么教程文章了,倒不是怕误人子弟,而是知道一定会误人子弟。有道是,写书的不做研究,做研究的不写书——这句话必须先做了研究才能理解呀。 题外话:我将会参加一月份的AMLD conference。 在茶话读到一篇喷Elsevier的文章,原版之前看过了。学术出版曾经是很专业的工作,手写的论文,出版商要转成铅字,包括数学公式也要帮你排好, 而且还不能犯错(意味着干活的人必须能看懂文章意思) ,到最后印刷成书,人力成本是很高的。 现在很多论文是由作者排版的(LaTeX),出版商的作用就削减了,包括现在还可以下载论文,连纸张都免了。所以大家就抗议价格太贵。 其实可以从政治的角度来看:学术论文是记载知识的,可知识的定义如此广泛,暂时没有用途的知识,将来可能会有价值。所以如果有一本万国期刊,允许所有人自由发表论文,后果就是上面会出现大量类似“吃什么可以祛痘”之类的文章。大家为啥不把文章发到那上面去呢?答案很简单:虽然你可以分辨论文的优劣,但当局分辨不了啊。 学术研究是要消耗经费的,因此提供经费的当局(通常是政府)必然要通过一定的标准来决定是否对一项研究提供经费。如果这个标准选得不好,就会造成经费的浪费。所以学术会议还是得开,论文集还是得出——作为学术界自身通过民主方式(审稿)决定谁能获得经费的一种制度。如果没有这个制度,大家都拿不到经费。大家给学术出版商支付的下载费用,可以视为一种税款,用于维持整个制度的运转。作为这个制度的运作载体,也就是期刊们——也就获得了巨大的政治力量,以至于收钱收得有些难看了(毕竟政府的反射弧长度是20光年)。




分类 ( Classification) 以及 分类器 ( Classifier)是机器学习和传统统计学里边一个很重要的概念,可能是我们日常生活中接触到的最多的一种机器学习应用。垃圾邮件过滤,人脸识别,手写字符识别,都是利用了分类的知识。 在这些任务中,我们往往希望给定一个具体输入,通过分类器的处理,能自动将其划到我们预先定义的某个类别中去。比如垃圾邮件过滤,给定邮件内容作为输入,输出 “是垃圾”,“不是垃圾” 两种状态之一;人脸识别,给定照片的特定区域,输出“是我”,“是路人甲”,“是路人A”,等等等等。 但实现这种自动分类功能往往并不简单,举例说,上述提到的几种输入数据,在概率上都是非常随机和分散的,难以抽取出一个统一的分类标准。具体一点,垃圾邮件的内容可能会千奇百怪,关键字五花八门;人脸更不用说,同样是两个鼻子一张嘴,但世间相貌岂止千万种,我自己作为脸盲患者就深感人脸识别不是一件易事;而人的字体和书写习惯也是非常难提炼出一个统一的标准,同样一个字,不同人写出来会非常不同。 但好在我们人类作为万物之灵,处理起这种任务起来虽然偶尔会有困难,但大部分时候可谓是得心应手,原因估计不说大家也知道,因为我们有学习能力。垃圾邮件收得多了,只看一个开头便随手扔进垃圾箱;在人事部混上一年,公司上下几百号面孔一个个熟络得不行;学习了书法鉴赏,几百张字帖看下来,看狂草也一点不虚。 但具体让你讲你怎么做到的,多数人也讲不出个所以然,认识很多张脸的人并不会说:”啊,其实我记下了每个的头围、眼间距、鼻子长度等等的详细尺寸,每次靠精确测量来一一对上号。“ 所以,人类的这种学习分类的能力,是一种模糊的过程,是在经历了足够多的输入作为 训练样本 后,慢慢总结、归纳出的一种概率模型,而非死板的映射。 机器学习中的分类器,就是通过数学工具,让机器也具备这种归纳总结数据的能力。而提到 通过数学工具归纳总结数据 ,难免会让人想到工程学中我们早已熟知的另一个概念——“拟合”。相信大部分人做过这种实验,先测量一堆数据,然后在坐标纸上一个一个画点,然后勾勾画画,这么就弄出来了一条曲线或者直线的模型。顺便一提,回想我大学,最令人发指的是学校竟然让用铅笔跟纸来做这个!美其名曰“熟悉具体步骤”,导致很多人一直到大四毕业设计才知道有 MATLAB 的拟合工具箱这个东西。 所以其实机器学习的很多理念,本质上还是在做 拟合 ( fitting),Andrew Ng 曾在一个讲座上开玩笑说,他最开始接触人工智能的时候,以为自己会接触各种神乎其技的科幻小说里的知识,结果发现他一直在做各种拟合。 而在他的CS229课程中,他讲的第一个线性分类器就是 线性回归 ( Linear Regression ),没错,就是线性回归,高中考纲,而且的的确确是有效果的。我硕士期间修的课程上,教授布置的第一个作业也是用线性回归预测总统大选( 用的伪数据)。




VC制作的软件,从VC2005之后的都需安装运行库才可以运行,从xp到win10一直都没改变,而且这货,上下版本还不能互相兼容orz这就是为啥在添加和删除程序里能看到各版本VC运行库的原因。 在某些情况下,比如绿色软件或者在pe下运行的软件,运行库可能带不全或者不好安装,于是制作一个不需要VC运行库的程序就是很有必要的了 首先创建一个VC2015的Win32 Project,选择默认选项创建一个窗体程序 打开菜单Project-> xxx Property(xxx是项目名)或者直接按Alt+F7, 选择Configuration为All Configuration, Platform为All Platform 将Basic Runtime Checks改为Default 将Security Check改为Disable Security Check(/GS-) 在stdafx.h的#progma once下面添加: #ifdef _DEBUG #pragma comment (linker, "/nodefaultlib:msvcrtd.lib") #else #pragma comment (linker, "/nodefaultlib:msvcrt.lib") #endif 这时候编译一般会出现一堆堆的错误 首先解决LNK2001 unresolved external symbol wWinMainCRTStartup, wWinMainCRTStartup是VC的CRT定义的Win32程序入口点,MFC程序为WinMainCRTStartup,控制台和DLL为mainCRTStartup和_DllMainCRTStartup。 没有了运行库就没有了入口点orz没有入口点就创建一个咯,wWinMainCRTStartup不接收任何参数,我写的入口如下,可以放在stdafx.cpp里: int __cdecl wWinMainCRTStartup() {     STARTUPINFO info;     info.cb = sizeof(STARTUPINFO);     GetStartupInfo(&info);        int ret = wWinMain(GetModuleHandle(NULL),         0,         GetCommandLine(),         info.dwFlags & STARTF_USESHOWWINDOW ? info.wShowWindow : SW_SHOWDEFAULT);     ExitProcess(ret);     return ret; } 之后应该就可以顺利编译工程了,这样制作出来的程序和静态链接的程序比起来,体积极小 本方法并不是完美的,有这些缺点: C++的异常不能用了, new delete也不能用了,也就是说这时候C++基本完全废了 C和C++的标准库大部分也都不能用了,不过这个有解决方法,我自己想出来的,可能不是很完美,就是手动实现某些stdlib里的函数,给了些常用的实现,都是互联网上搜索的: void* __cdecl malloc(ULONG_PTR uSize) {     return (void *)HeapAlloc(GetProcessHeap(), 0, uSize); }    void __cdecl free(LPVOID pMemBlock) {     HeapFree(GetProcessHeap(), 0, (LPVOID)pMemBlock); } #pragma function(memcpy) void* memcpy(void * dst, const void * src, size_t count) {     void * ret = dst;    #if defined (_M_IA64)     {         extern void RtlMoveMemory(void *, const void *, size_t count);            RtlMoveMemory(dst, src, count);     } #else /* defined (_M_IA64) */     /*     * copy from lower addresses to higher addresses     */     while (count--) {         *(char *)dst = *(char *)src;         dst = (char *)dst + 1;         src = (char *)src + 1;     } #endif /* defined (_M_IA64) */        return(ret); }    #pragma function(memset) void* __cdecl memset(void* src, int c, size_t count) {     char *tmpsrc = (char*)src;     while (count--)         *tmpsrc++ = (char)c;     return src; } 有的时候编译会看到俩错误,找不到__imp__invalid_parameter_noinfo和__imp__errno,并不晓得这俩是啥,于是空函数代替之: extern "C" void __cdecl __imp__invalid_parameter_noinfo() {    }    extern "C" void __cdecl __imp__errno() {    } 还有个说不清的问题,就是调用COM组件的时候有点奇怪。。 还有,MFC不适用!也就是说,复杂的软件就不要用这种方法咯 参考 http://bbs.kechuang.org/read/80005 http://stackoverflow.com/questions/2938966/how-to-use-vc-intrinsic-functions-w-o-run-time-library


心血来潮,决定把上次搞得SVPWM算法的推导过程整理一下,然后.....今天整整一天都在干这个.............. 功夫不负有心人,终于搞出来个不像科普文章也不像论文的怪文........... 各位大神轻点拍砖啊........... 以下由bg8npk搬运 搞SVPWM的目的呢 ,其实就是想让电机平稳地旋转,这次主要说说三相同步电动机(PMSM)的SVPWM控制吧(三相电机结构还请各位自行google)。 PMSM定子的三个绕组嘛,其实可以看做三个电磁铁,而且它们的方向互相成120°角,而它们产生的磁场强度呢,又是和通过它们的电流成正比的,也就是说,我们可以分别控制三个绕组的电流,来分别控制三个电磁铁的磁场强度,进而在电动机内叠加出一个磁场矢量。 PMSM的转子我们可以看做一个简单的永磁体,而上面那个磁场矢量可以看做另一个磁铁,所以,三相同步电机的原理通俗的讲呢, 就是两个条形磁铁摞一块,转动上面那个,下面的也跟着转 ……… 而为了能让电机平稳地转动,我们希望电机的定子磁场矢量能以恒定的速度旋转,并且保持磁场强度大小不变。前面说到,磁场强度是和绕组电流成正比的,所以呢,我们需要一个在空间中匀速旋转并且大小不变的电流矢量(电流矢量是个比较抽象的概念,其实它是由三相绕组的三个电流叠加起来的,稍后会详解)。 假设某一时刻,这个电流矢量是这么个情况: 其中坐标系中α轴与电机的A相绕组的方向一致,β轴与α轴成90°角 那么经过一段很小的时间△t之后,这个电流矢量又旋转了一点点: 由向量的加法运算可知,为了让电流矢量实现这个旋转,我们需要在这个△t中给它施加一个△I: 如果我们忽略电动机绕组的内阻的话,电动机其实可以近似为一个感性的负载,那么对于一个电感,如果给它加上恒定的电压,他的电流会随着时间线性增长,所以,为了能给电动机施加这个△I,我们只需要用一个和△I方向相同的电压矢量,再作用△t的时间,就OK啦。 恩,接下来的一个△t也是差不多这么个情况,有点不同的是△I的方向也转了一点点。随着一个一个△t转下去,电流矢量会转上一整圈,然后△I也就会转上一整圈。当我们把△t取的足够小的时候,三角形I也就进化成为一个在空间中匀速旋转的矢量。 由此可见,我们只要给PMSM施加一个大小不变,匀速旋转的电压矢量,就可以在PMSM内部形成一个大小不变,匀速旋转的电流矢量,继而再形成一个大小不变,匀速旋转的磁场矢量*(相信很多人已经有点晕了……其实我也晕……..)* 接下来的问题就是………怎么通过控制三相绕组上的电压,才能叠加出我们想要的电压矢量呢? 首先呢,先来一张三相全桥的电路图: 恩,如果我们让一个桥臂输出高压,另两个桥臂输出0,那么我们可以得到三个互成120°角的电压矢量: 如果让两个桥臂输出高压,另一个输出0,那么我们又可以得到三个电压矢量,相当于两个基本电压矢量的叠加: 如果三个桥臂同时输出0或高压,PMSM的相电压为0,所以我们又得到两个零矢量。 可以看出,除了两个零向量外的六个矢量把整个平面分成了6个扇区,在任意时刻,旋转的电压矢量肯定会落在某一个扇区里。我们给这几个扇区编一下号: 其中U0 U2 U4这三个向量称为主向量,U1,U3,U5称为辅向量。易知U0-U5的幅度都相同,均为三相全桥的直流供电电压,设这个电压为Udc。 SVPWM的基本实现方法呢,**就是在一个PWM周期内。调节一个扇区两边的两个电压矢量和两个零矢量的作用时间,根据平均值等效原理,来合成出我们想要的电压矢量。**假设我们想要的电压矢量正好落在第一扇区里面,那么大致情况就是这样滴: 恩,设一个PWM周期为T,主向量U0作用时间为T1,辅向量U1作用时间为T2,零向量作用时间为T0,那么有T=T0+T1+T2 由向量的叠加与分解可知,我们要输出的Uout在两个坐标轴上的分量分别等于主向量辅向量经过平均值等效后的两个向量在两个坐标轴上的分量之和……好吧我承认我语文不好…….. 那么就有如下关系 Uout * COSθ =(T1/T)Udc+(T2/T)Udc * COS 60° Uout * SINθ =(T2/T)Udc * SIN 60° T0 = T-T1-T2 令Uα=Uout * COSθ Uβ=Uout * SINθ 解得 T0=(1-Uα/Udc -Uβ/√3*Udc)T T1=(Uα/Udc – Uβ/√3*Udc)T T2=2 Uβ T/√3*Udc 再令X=Uα T/Udc Y=Uβ T/√3*Udc 就有 T0=T-X-Y T1=X-Y T2=2*Y 设A B C三相全桥三个桥臂的占空比分别为D1 D2 D3,那么输出U1只需要A相桥臂输出高压其他两个桥臂输出0就行,U2需要A B两个桥臂输出高压,C相输出0,零向量可以让三个桥臂同时输出高压或0,一般情况下我们让两个零向量的作用时间相同,那么就有 D1=(T0/2 + T1 + T2)/T=(1+X+Y)/2 D2=(T0/2 + T2)/T=(1-X+3Y)/2 D3=T0/2*T=(1-X-Y)/2 恩,第一个扇区的推导就是这样,后面的五个扇区的推导过程类似,实在不想挨个把五个扇区的详细推导都打上来了…….太多了,接下来就把结果写上来吧 第二个扇区: D1=1/2 +X D2=1/2 +Y D3=1/2 –Y 第三个扇区 D1=(1+X-Y)/2 D2=(1-X+Y)/2 D3=(1-X-3*Y)/2 第四个扇区 D1=(1+X+Y)/2 D2= (1-X+3Y)/2 D3=(1-X-Y)/2 第五个扇区: D1=1/2 +X D2=1/2 +Y D3=1/2 –Y 第六个扇区 D1=(1+X-Y)/2 D2=(1-X+Y)/2 D3=(1-X-3*Y)/2 其实我们可以发现,第一第四扇区、第二第五扇区还有第三第六扇区的占空比都是相同的,有了这些占空比,我们就可以很方便的写出SVPWM程序了 最后附上C演示程序 #include <stdio.h> #include <math.h> #define PI 3.141592653 #define SQRT_3 1.732051 #define DEPTH 256 /*数据深度,即存储单元的个数*/ #define WIDTH 8 /*存储单元的宽度*/ void svpwm(void); unsigned int Sampling = 256; unsigned char CCR1, CCR2, CCR3; double x,y; double PI2; double cosa; double buffer[6]; double Vdc = 100; double Vo = SQRT_3*Vdc/2; int i; int s1,s2,s3,s4,s5; void init(void) { PI2 = PI * 2 / Sampling; s1 = Sampling/6; s2 = Sampling/3; s3 = Sampling/2; s4 = 2*Sampling/3; s5 = 5*Sampling/6; cosa = 2*cos(PI2); buffer[0] = -sin(PI2) *(Vo / Vdc) /(2* SQRT_3); buffer[1] = 0; buffer[2] = 0; buffer[3] = cos(PI2) * (Vo / Vdc)/2; buffer[4] = Vo / (Vdc*2); buffer[5] = Vo / (Vdc*2); } int main(void) { init( ); for (i = 0; i < Sampling; i++) { svpwm( ); printf("%d %d %d %d \n", i,CCR1,CCR2,CCR3); } } while(1); } void svpwm(void) { x = buffer[5]; y = buffer[2]; buffer[2] = cosa * buffer[1] - buffer[0]; buffer[5] = cosa * buffer[4] - buffer[3]; buffer[0] = buffer[1]; buffer[1] = buffer[2]; buffer[3] = buffer[4]; buffer[4] = buffer[5]; if (i <= s1) //判断扇区,计算占空比 { CCR1 = (0.5 + x + y)*256+256; CCR2 = (0.5 - x + 3*y)*256+256; CCR3 = (0.5 - x - y)*256+256; } else if (i <= s2) { CCR1 = (0.5 + 2*x)*256+256; CCR2 = (0.5 + 2*y)*256+256; CCR3 = (0.5 - 2*y)*256+256; } else if (i <= s3) { CCR1 = (0.5 + x - y)*256+256; CCR2 = (0.5 - x + y)*256+256; CCR3 = (0.5 - x - 3*y)*256+256; } else if (i <= s4) { CCR1 = (0.5 + x + y)*256+256; CCR2 = (0.5 - x + 3*y)*256+256; CCR3 = (0.5 - x - y)*256+256; } else if (i <= s5) { CCR1 = (0.5 + 2*x)*256+256; CCR2 = (0.5 + 2*y)*256+256; CCR3 = (0.5 - 2*y)*256+256; } else { CCR1 = (0.5 + x - y)*256+256; CCR2 = (0.5 - x + y)*256+256; CCR3 = (0.5 - x - 3*y)*256+256; } }


在我的上一篇文章末尾( http://bbs.kechuang.org/read/79505 ),我简要论述了“硬件安全模块”对互联网信息安全的重要性。如今,无数的银行业、支付业者使用HSM保证它们的业务不受黑客的破坏。 我提出的问题是:硬件安全模块能自己做吗? 在回答这个问题之前,不妨看看以下两篇文章。 http://netsecurity.51cto.com/art/201005/200658.htm http://www.safeploy.com/2014/12-4/20564851725.html 结合我的上一篇文章(如果没有读过,建议先读一下),一个最简单的实用HSM大概是这样工作的: 1)通过一定的接口与计算机通信 2)内部保存有私钥,且无法通过任何暴力手段获得该私钥 3)能够利用内部保存的私钥,签发证书,此证书可由对应的公钥验证。 其实,我们常说的“加密狗” “U盾”都属于HSM,但它们一般只能保存证书(而且不算特别安全),不一定具有签发证书的功能。 为此我画了一个草图。 是不是有点像树莓派?其实就是一个网络服务器,可以利用内置的OpenSSL软件签发证书,并返回给用户。 附加要求挺简单: 1)只能通过网络与外界联系,不允许通过其他方式访问。 网络接口对外必须不暴露任何漏洞,例如恶意流量导致的缓冲区溢出,等等。 2)盒子非常坚固,难以破坏、拆卸;即便拆开,也无法获取到内部存储的密钥。 3)无法从盒子之外通过各种窥探方式(例如电流监测、电磁辐射监测)有效地获知盒子内部的工作状态,也即无法窥探出密钥。 基于这样的HSM模块,我们可以涉足许多有意思的领域,例如谍报、保密,还有当下最火的支付业务。




一直有打造一台计算机系统的想法,恰巧今年有机会,就开始了设计。我的目标是能够运行,用74门搭建,而不仅仅是仿真或在FPGA上运行(主要是我不会hh)。构思了一段时间,把整台机器的架构稍微梳理了一下。画了一个思维导图,大家凑合看。涉及到运算,一定会有位宽这一概念。这台计算机设计出来的目标是运行,不仅仅是软件上的设计,于是74门的数量就是个大问题。大家知道,计算机的位宽增加,晶体管的数量必然成指数型增加,划不来。现在拿性能换成本,抛弃性能,做成一位的,74门数量可以做到几十片甚至十几片。 首先是CPU 实际上,CPU的开发工作在几个月前就开始了,几年前也有过做CPU的想法,也构思过一段时间。成本限制,现在目前设计了16位程序指针,可以寻址64k字节的程序代码。CPU涉及到跳转指令,所以使用74hc161这种可预置的二进制计数器作为程序指针,共需4片。cpu采用超精简设计,由于只有一位位宽,在操作内存过程中没有字节的概念,直接操作位。这样虽然效率较低,但硬件简便。CPU没有片上alu,只提供立即数放内存、读取内存、内存移内存精简指令,把alu逻辑运算单元当作内存的一部分操作,如思维导图。cpu使用16位指令集,低八位数据,高八位指令。低八位在跳转指令时先分别向两个程序地址寄存器中送入数据,耗时两个时钟,再跳转,耗时两个时钟。共耗时四个时钟。 片内所有的寄存器,包括内存数据暂存器、指令暂存器、两片程序地址暂存器,都使用了74hc273这种上升沿D触发器。指令译码部分其实有点像自动机,每次译码要先给主时钟,PC+1,等地址和数据建立完毕后再拉高指令锁存,然后全0为不动作(空指令),看哪位有1就执行相应的动作。按照这个逻辑,指令锁存要比PC时钟迟一些到来,所以给指令暂存器的锁存端口加一个非门。这样主时钟上升时建立地址,稳定后时钟下降,地址锁住不变,此时对于指令暂存器来说时钟是上升的,指令被锁住,完成译码。 构思CPU有三种跳转方式,分别是目标地址跳转、DB总线接口为1跳转、DB总线接口为0跳转。目标地址跳转需要两个时钟周期,判断跳转需要1+2=3个时钟周期,若不跳转只消耗2个时钟周期。因为内存里的数据需要读取到暂存器,所以这会占用一个时钟。判断跳转那个地方使用四个与非门,根据电路图不难了解原理。 CPU设计完毕后打了一张板子,把基本的功能之类都测试一遍,发现功能正常。下面放一波图 图为测试板 测试得出一点:所有把数据移入某个寄存器的指令后面必须不能是把数据移入这个寄存器的指令(例如数据移ra后,下一个时钟周期不能再次移ra),因为移入时寄存器的时钟需要上升沿才能有效,而两次移入会导致时钟没有拉低缓冲时间。 将CPU的指令汇总一下,就有了指令集。真正的精简指令,只有10条(也可以认为是我偷懒了 RA是高八位地址寄存器 RB是低八位地址寄存器 xx表示任意数据 aa表示地址数据 高位 低位 单字节: 0x00 0xxx 空指令 0x80 0xxx DB为1时跳转到RA RB 0x40 0xxx DB为0时跳转到RA RB 0xC0 0xxx 无条件跳转到RA RB 0x20 0xaa 地址数据送RA 0x10 0xaa 地址数据送RB 0x08 0xxx RARB指向RAM送RC 双字节: 0x04 0xxx┐ 0x06 0xxx┴ RARB指向RAM置0 0x05 0xxx┐ 0x07 0xxx┴ RARB指向RAM置1 0x00 0xxx┐ 0x02 0xxx┴ RC送RARB指向RAM 注:RAM区不使用标准结构,使用上升沿D锁存器构成储存单元,寻址空间0~2^16-1=0~65535 然后就是构成计算机系统了。RAM使用74HC74+(74hc138+4016传输门)译码,ROM是at28c256。这些确定了就可以打主板了。没想着做多少性能,就只用了4片74HC74构成1*8的内存空间,ROM有16*32K空间,两者最多均可寻址64K。io部分就偷懒吧,直接把内存的输出引出来,接个led也可以显示工作状态,没有设计输入接口。 这是整个计算机系统的pcb,已经设计好送去打样了,还没到手。布线很乱,大家也不要喷了,因为这个电路相对来说还是有点麻烦的,于是自动布线爽歪歪。毕竟板子不想跑高频,主频10K差不多了,虽然74门最多可以支持10M,不过也无所谓啦。现在板子上不带时钟,用按钮按,时钟有引出,可以外加。 好吧,今天先写这么多,等全部测试完毕后再来补吧。电路图不在身边,下次再发。







之所以说是常规加速版,是因为DX11还有GPGPU计算加速功能Compute Shader,也就是所谓的DirectCompute,但是这里我们只用到了常规的图形流水线加速功能。 最低配置被设置为D3D_FEATURE_LEVEL_9_3和"ps_4_0_level_9_3"。如果将循环次数改多一点,由于"ps_4_0_level_9_3"不包含条件循环指令,循环操作实际上被展开了,指令数量很容易超过限制,这个时候就需要把最低配置改为D3D_FEATURE_LEVEL_10_0和"ps_4_0"。 // shaders.txt struct VS_OUTPUT { float4 pos : SV_POSITION; float2 tex : TEXCOORD; }; void vs_common(float3 pos : POSITION, float2 tex : TEXCOORD, out VS_OUTPUT vso) { vso.pos = float4(pos, 1.0f); vso.tex = tex; } float4 ps_mandelbrot(VS_OUTPUT vso) : SV_TARGET { float2 zvar = float2(0, 0); float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + vso.tex.x; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + vso.tex.y; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } float4 ps_julia(VS_OUTPUT vso) : SV_TARGET { float2 zvar = vso.tex; float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + 0.4f; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + 0.3f; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } // main.c - 使用了C99语法,可使用VS2013-2017编译 #undef UNICODE #define UNICODE 1 #include <windows.h> #include <assert.h> #include <d3d11.h> #include <d3dcompiler.h> #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "d3dcompiler.lib") #define NOFAIL(x) assert(SUCCEEDED(x)) #define NOTZERO(x) assert(x) #define CLEANUP(x) if (x) { (x)->lpVtbl->Release(x); (x) = NULL; } HINSTANCE g_hinst; HWND g_hmainwnd; // DirectX D3D_FEATURE_LEVEL g_fl; ID3D11Device *g_dev; ID3D11DeviceContext *g_immctx; IDXGISwapChain *g_swpch; ID3D11Texture2D *g_backbuffer; ID3D11RenderTargetView *g_backbuffer_rtv; // DirectX资源 ID3D11VertexShader *g_vs_common; ID3D11PixelShader *g_ps_mandelbrot; ID3D11PixelShader *g_ps_julia; ID3D11PixelShader *g_ps_current; ID3D11InputLayout *g_inlayout; ID3D11Buffer *g_vertexbuffer; LRESULT MainWnd_OnCreate(HWND hWnd, LPCREATESTRUCT lpcs) { // 创建DirectX RECT rc; GetClientRect(hWnd, &rc); DXGI_MODE_DESC backbuf_mode_desc = { rc.right - rc.left, rc.bottom - rc.top, { 60, 1 }, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCALING_UNSPECIFIED, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, }; DXGI_SWAP_CHAIN_DESC swpch_desc = { backbuf_mode_desc, { 1, 0 }, DXGI_USAGE_RENDER_TARGET_OUTPUT, 1, hWnd, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0, }; D3D_FEATURE_LEVEL fllist[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, }; if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, fllist, sizeof fllist / sizeof fllist[0], D3D11_SDK_VERSION, &swpch_desc, &g_swpch, &g_dev, &g_fl, &g_immctx))) { NOFAIL(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, fllist, sizeof fllist / sizeof fllist[0], D3D11_SDK_VERSION, &swpch_desc, &g_swpch, &g_dev, &g_fl, &g_immctx)); } IDXGIFactory *dxgifac; NOFAIL(g_swpch->lpVtbl->GetParent(g_swpch, &IID_IDXGIFactory, (void**)&dxgifac)); NOFAIL(dxgifac->lpVtbl->MakeWindowAssociation(dxgifac, hWnd, DXGI_MWA_NO_ALT_ENTER)); CLEANUP(dxgifac); NOFAIL(g_swpch->lpVtbl->GetBuffer(g_swpch, 0, &IID_ID3D11Texture2D, (void**)&g_backbuffer)); NOFAIL(g_dev->lpVtbl->CreateRenderTargetView(g_dev, (ID3D11Resource *)g_backbuffer, NULL, &g_backbuffer_rtv)); // 创建绘制资源 // 读取shaders.txt static char shaders_txt[65536]; memset(shaders_txt, 0, sizeof shaders_txt); HANDLE hfile = CreateFile(L"shaders.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hfile == INVALID_HANDLE_VALUE) { MessageBox(hWnd, L"不能打开shader.txt文件", L"文件错误", MB_OK); DestroyWindow(hWnd); return 0; } DWORD dwret; ReadFile(hfile, shaders_txt, sizeof shaders_txt - 1, &dwret, NULL); CloseHandle(hfile); // 编译shaders.txt并创建相关资源 ID3DBlob *bcode_ps_mandelbrot, *bcode_ps_julia, *bcode_vs_common, *errmsg; if (FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "ps_mandelbrot", "ps_4_0_level_9_3", 0, 0, &bcode_ps_mandelbrot, &errmsg)) || FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "ps_julia", "ps_4_0_level_9_3", 0, 0, &bcode_ps_julia, &errmsg)) || FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "vs_common", "vs_4_0_level_9_3", 0, 0, &bcode_vs_common, &errmsg))) { NOTZERO(errmsg); MessageBoxA(hWnd, errmsg->lpVtbl->GetBufferPointer(errmsg), "D3DCompile Error", MB_OK); CLEANUP(errmsg); CLEANUP(bcode_ps_mandelbrot); CLEANUP(bcode_ps_julia); CLEANUP(bcode_vs_common); DestroyWindow(hWnd); return 0; } NOFAIL(g_dev->lpVtbl->CreatePixelShader(g_dev, bcode_ps_mandelbrot->lpVtbl->GetBufferPointer(bcode_ps_mandelbrot), bcode_ps_mandelbrot->lpVtbl->GetBufferSize(bcode_ps_mandelbrot), NULL, &g_ps_mandelbrot)); NOFAIL(g_dev->lpVtbl->CreatePixelShader(g_dev, bcode_ps_julia->lpVtbl->GetBufferPointer(bcode_ps_julia), bcode_ps_julia->lpVtbl->GetBufferSize(bcode_ps_julia), NULL, &g_ps_julia)); NOFAIL(g_dev->lpVtbl->CreateVertexShader(g_dev, bcode_vs_common->lpVtbl->GetBufferPointer(bcode_vs_common), bcode_vs_common->lpVtbl->GetBufferSize(bcode_vs_common), NULL, &g_vs_common)); D3D11_INPUT_ELEMENT_DESC inputelements[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; NOFAIL(g_dev->lpVtbl->CreateInputLayout(g_dev, inputelements, sizeof inputelements / sizeof inputelements[0], bcode_vs_common->lpVtbl->GetBufferPointer(bcode_vs_common), bcode_vs_common->lpVtbl->GetBufferSize(bcode_vs_common), &g_inlayout)); CLEANUP(bcode_ps_mandelbrot); CLEANUP(bcode_ps_julia); CLEANUP(bcode_vs_common); float vertexdata[] = { -1.0f, -1.0f, 1.0f, -2.2f, -2.2f, -1.0f, 1.0f, 1.0f, -2.2f, 2.2f, 1.0f, -1.0f, 1.0f, 2.2f, -2.2f, 1.0f, 1.0f, 1.0f, 2.2f, 2.2f, }; D3D11_BUFFER_DESC bd = { sizeof vertexdata, D3D11_USAGE_DEFAULT, 0, 0, 0 }; D3D11_SUBRESOURCE_DATA srd = { vertexdata, 0, 0 }; NOFAIL(g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &srd, &g_vertexbuffer)); g_ps_current = g_ps_mandelbrot; return 0; } LRESULT MainWnd_OnDestroy(HWND hWnd) { if (g_immctx) g_immctx->lpVtbl->ClearState(g_immctx); // 清理资源 CLEANUP(g_vertexbuffer); CLEANUP(g_inlayout); CLEANUP(g_vs_common); CLEANUP(g_ps_julia); CLEANUP(g_ps_mandelbrot); // 清理DirectX CLEANUP(g_backbuffer_rtv); CLEANUP(g_backbuffer); CLEANUP(g_swpch); CLEANUP(g_immctx); CLEANUP(g_dev); // 产生WM_QUIT消息,结束消息循环 PostQuitMessage(0); return 0; } LRESULT MainWnd_OnSize(HWND hWnd, WPARAM wParam, POINTS pts) { if (g_swpch && g_immctx && g_dev) { g_immctx->lpVtbl->ClearState(g_immctx); CLEANUP(g_backbuffer_rtv); CLEANUP(g_backbuffer); RECT rc; GetClientRect(hWnd, &rc); NOFAIL(g_swpch->lpVtbl->ResizeBuffers(g_swpch, 1, rc.right - rc.left, rc.bottom - rc.top, DXGI_FORMAT_UNKNOWN, 0)); NOFAIL(g_swpch->lpVtbl->GetBuffer(g_swpch, 0, &IID_ID3D11Texture2D, (void**)&g_backbuffer)); NOFAIL(g_dev->lpVtbl->CreateRenderTargetView(g_dev, g_backbuffer, NULL, &g_backbuffer_rtv)); } return 0; } LRESULT MainWnd_OnPaint(HWND hWnd) { // GDI例行任务 PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); // DirectX任务 // 清除背景 float backcolor[] = { 0.0f, 1.0f, 1.0f, 1.0f }; g_immctx->lpVtbl->ClearRenderTargetView(g_immctx, g_backbuffer_rtv, backcolor); // 设置状态并绘图 RECT rc; GetClientRect(hWnd, &rc); float ratio = (float)(rc.right - rc.left) / (float)(rc.bottom - rc.top); float vertexdata[] = { -1.0f, -1.0f, 1.0f, -2.2f * ratio, -2.2f, -1.0f, 1.0f, 1.0f, -2.2f * ratio, 2.2f, 1.0f, -1.0f, 1.0f, 2.2f * ratio, -2.2f, 1.0f, 1.0f, 1.0f, 2.2f * ratio, 2.2f, }; g_immctx->lpVtbl->UpdateSubresource(g_immctx, (ID3D11Resource *)g_vertexbuffer, 0, NULL, vertexdata, 0, 0); UINT stride = sizeof(float) * 5, offset = 0; g_immctx->lpVtbl->IASetVertexBuffers(g_immctx, 0, 1, &g_vertexbuffer, &stride, &offset); g_immctx->lpVtbl->IASetInputLayout(g_immctx, g_inlayout); g_immctx->lpVtbl->IASetPrimitiveTopology(g_immctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); g_immctx->lpVtbl->VSSetShader(g_immctx, g_vs_common, NULL, 0); D3D11_VIEWPORT viewport = { 0.0f, 0.0f, rc.right - rc.left, rc.bottom - rc.top, 0.0f, 1.0f }; g_immctx->lpVtbl->RSSetViewports(g_immctx, 1, &viewport); g_immctx->lpVtbl->PSSetShader(g_immctx, g_ps_current, NULL, 0); g_immctx->lpVtbl->OMSetRenderTargets(g_immctx, 1, &g_backbuffer_rtv, NULL); g_immctx->lpVtbl->Draw(g_immctx, 4, 0); // 上屏 NOFAIL(g_swpch->lpVtbl->Present(g_swpch, 0, 0)); return 0; } LRESULT MainWnd_OnLButtonDown(HWND hWnd, WPARAM wParam, POINTS pts) { g_ps_current = g_ps_mandelbrot; InvalidateRect(hWnd, NULL, FALSE); return 0; } LRESULT MainWnd_OnRButtonDown(HWND hWnd, WPARAM wParam, POINTS pts) { g_ps_current = g_ps_julia; InvalidateRect(hWnd, NULL, FALSE); return 0; } LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: return MainWnd_OnCreate(hWnd, (LPCREATESTRUCT)lParam); case WM_DESTROY: return MainWnd_OnDestroy(hWnd); case WM_SIZE: return MainWnd_OnSize(hWnd, wParam, MAKEPOINTS(lParam)); case WM_LBUTTONDOWN: return MainWnd_OnLButtonDown(hWnd, wParam, MAKEPOINTS(lParam)); case WM_RBUTTONDOWN: return MainWnd_OnRButtonDown(hWnd, wParam, MAKEPOINTS(lParam)); case WM_PAINT: return MainWnd_OnPaint(hWnd); default: return DefWindowProc(hWnd, msg, wParam, lParam); } } int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR szCmdLine, int nShowCmd) { WNDCLASSEX wcex = { sizeof wcex, CS_VREDRAW|CS_HREDRAW, WndProc, 0, 0, g_hinst = hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), NULL/*空画刷防闪烁*/, NULL, L"MainWndClass", LoadIcon(NULL, IDI_APPLICATION) }; RegisterClassEx(&wcex); RECT rc = { 0, 0, 480, 480 }; AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); g_hmainwnd = CreateWindowEx(0, wcex.lpszClassName, L"D3D11主窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL); ShowWindow(g_hmainwnd, nShowCmd); UpdateWindow(g_hmainwnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }

之所以说是常规加速版,是因为DX11还有GPGPU计算加速功能Compute Shader,也就是所谓的DirectCompute,但是这里我们只用到了常规的图形流水线加速功能。 最低配置被设置为D3D_FEATURE_LEVEL_9_3和"ps_4_0_level_9_3"。如果将循环次数改多一点,由于"ps_4_0_level_9_3"不包含条件循环指令,循环操作实际上被展开了,指令数量很容易超过限制,这个时候就需要把最低配置改为D3D_FEATURE_LEVEL_10_0和"ps_4_0"。 // shaders.txt struct VS_OUTPUT { float4 pos : SV_POSITION; float2 tex : TEXCOORD; }; void vs_common(float3 pos : POSITION, float2 tex : TEXCOORD, out VS_OUTPUT vso) { vso.pos = float4(pos, 1.0f); vso.tex = tex; } float4 ps_mandelbrot(VS_OUTPUT vso) : SV_TARGET { float2 zvar = float2(0, 0); float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + vso.tex.x; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + vso.tex.y; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } float4 ps_julia(VS_OUTPUT vso) : SV_TARGET { float2 zvar = vso.tex; float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + 0.4f; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + 0.3f; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } // main.c - 使用了C99语法,可使用VS2013-2017编译 #undef UNICODE #define UNICODE 1 #include <windows.h> #include <assert.h> #include <d3d11.h> #include <d3dcompiler.h> #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "d3dcompiler.lib") #define NOFAIL(x) assert(SUCCEEDED(x)) #define NOTZERO(x) assert(x) #define CLEANUP(x) if (x) { (x)->lpVtbl->Release(x); (x) = NULL; } HINSTANCE g_hinst; HWND g_hmainwnd; // DirectX D3D_FEATURE_LEVEL g_fl; ID3D11Device *g_dev; ID3D11DeviceContext *g_immctx; IDXGISwapChain *g_swpch; ID3D11Texture2D *g_backbuffer; ID3D11RenderTargetView *g_backbuffer_rtv; // DirectX资源 ID3D11VertexShader *g_vs_common; ID3D11PixelShader *g_ps_mandelbrot; ID3D11PixelShader *g_ps_julia; ID3D11PixelShader *g_ps_current; ID3D11InputLayout *g_inlayout; ID3D11Buffer *g_vertexbuffer; LRESULT MainWnd_OnCreate(HWND hWnd, LPCREATESTRUCT lpcs) { // 创建DirectX RECT rc; GetClientRect(hWnd, &rc); DXGI_MODE_DESC backbuf_mode_desc = { rc.right - rc.left, rc.bottom - rc.top, { 60, 1 }, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCALING_UNSPECIFIED, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, }; DXGI_SWAP_CHAIN_DESC swpch_desc = { backbuf_mode_desc, { 1, 0 }, DXGI_USAGE_RENDER_TARGET_OUTPUT, 1, hWnd, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0, }; D3D_FEATURE_LEVEL fllist[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, }; if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, fllist, sizeof fllist / sizeof fllist[0], D3D11_SDK_VERSION, &swpch_desc, &g_swpch, &g_dev, &g_fl, &g_immctx))) { NOFAIL(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, fllist, sizeof fllist / sizeof fllist[0], D3D11_SDK_VERSION, &swpch_desc, &g_swpch, &g_dev, &g_fl, &g_immctx)); } IDXGIFactory *dxgifac; NOFAIL(g_swpch->lpVtbl->GetParent(g_swpch, &IID_IDXGIFactory, (void**)&dxgifac)); NOFAIL(dxgifac->lpVtbl->MakeWindowAssociation(dxgifac, hWnd, DXGI_MWA_NO_ALT_ENTER)); CLEANUP(dxgifac); NOFAIL(g_swpch->lpVtbl->GetBuffer(g_swpch, 0, &IID_ID3D11Texture2D, (void**)&g_backbuffer)); NOFAIL(g_dev->lpVtbl->CreateRenderTargetView(g_dev, (ID3D11Resource *)g_backbuffer, NULL, &g_backbuffer_rtv)); // 创建绘制资源 // 读取shaders.txt static char shaders_txt[65536]; memset(shaders_txt, 0, sizeof shaders_txt); HANDLE hfile = CreateFile(L"shaders.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hfile == INVALID_HANDLE_VALUE) { MessageBox(hWnd, L"不能打开shader.txt文件", L"文件错误", MB_OK); DestroyWindow(hWnd); return 0; } DWORD dwret; ReadFile(hfile, shaders_txt, sizeof shaders_txt - 1, &dwret, NULL); CloseHandle(hfile); // 编译shaders.txt并创建相关资源 ID3DBlob *bcode_ps_mandelbrot, *bcode_ps_julia, *bcode_vs_common, *errmsg; if (FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "ps_mandelbrot", "ps_4_0_level_9_3", 0, 0, &bcode_ps_mandelbrot, &errmsg)) || FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "ps_julia", "ps_4_0_level_9_3", 0, 0, &bcode_ps_julia, &errmsg)) || FAILED(D3DCompile(shaders_txt, strlen(shaders_txt), "shaders.txt", NULL, NULL, "vs_common", "vs_4_0_level_9_3", 0, 0, &bcode_vs_common, &errmsg))) { NOTZERO(errmsg); MessageBoxA(hWnd, errmsg->lpVtbl->GetBufferPointer(errmsg), "D3DCompile Error", MB_OK); CLEANUP(errmsg); CLEANUP(bcode_ps_mandelbrot); CLEANUP(bcode_ps_julia); CLEANUP(bcode_vs_common); DestroyWindow(hWnd); return 0; } NOFAIL(g_dev->lpVtbl->CreatePixelShader(g_dev, bcode_ps_mandelbrot->lpVtbl->GetBufferPointer(bcode_ps_mandelbrot), bcode_ps_mandelbrot->lpVtbl->GetBufferSize(bcode_ps_mandelbrot), NULL, &g_ps_mandelbrot)); NOFAIL(g_dev->lpVtbl->CreatePixelShader(g_dev, bcode_ps_julia->lpVtbl->GetBufferPointer(bcode_ps_julia), bcode_ps_julia->lpVtbl->GetBufferSize(bcode_ps_julia), NULL, &g_ps_julia)); NOFAIL(g_dev->lpVtbl->CreateVertexShader(g_dev, bcode_vs_common->lpVtbl->GetBufferPointer(bcode_vs_common), bcode_vs_common->lpVtbl->GetBufferSize(bcode_vs_common), NULL, &g_vs_common)); D3D11_INPUT_ELEMENT_DESC inputelements[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; NOFAIL(g_dev->lpVtbl->CreateInputLayout(g_dev, inputelements, sizeof inputelements / sizeof inputelements[0], bcode_vs_common->lpVtbl->GetBufferPointer(bcode_vs_common), bcode_vs_common->lpVtbl->GetBufferSize(bcode_vs_common), &g_inlayout)); CLEANUP(bcode_ps_mandelbrot); CLEANUP(bcode_ps_julia); CLEANUP(bcode_vs_common); float vertexdata[] = { -1.0f, -1.0f, 1.0f, -2.2f, -2.2f, -1.0f, 1.0f, 1.0f, -2.2f, 2.2f, 1.0f, -1.0f, 1.0f, 2.2f, -2.2f, 1.0f, 1.0f, 1.0f, 2.2f, 2.2f, }; D3D11_BUFFER_DESC bd = { sizeof vertexdata, D3D11_USAGE_DEFAULT, 0, 0, 0 }; D3D11_SUBRESOURCE_DATA srd = { vertexdata, 0, 0 }; NOFAIL(g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &srd, &g_vertexbuffer)); g_ps_current = g_ps_mandelbrot; return 0; } LRESULT MainWnd_OnDestroy(HWND hWnd) { if (g_immctx) g_immctx->lpVtbl->ClearState(g_immctx); // 清理资源 CLEANUP(g_vertexbuffer); CLEANUP(g_inlayout); CLEANUP(g_vs_common); CLEANUP(g_ps_julia); CLEANUP(g_ps_mandelbrot); // 清理DirectX CLEANUP(g_backbuffer_rtv); CLEANUP(g_backbuffer); CLEANUP(g_swpch); CLEANUP(g_immctx); CLEANUP(g_dev); // 产生WM_QUIT消息,结束消息循环 PostQuitMessage(0); return 0; } LRESULT MainWnd_OnSize(HWND hWnd, WPARAM wParam, POINTS pts) { if (g_swpch && g_immctx && g_dev) { g_immctx->lpVtbl->ClearState(g_immctx); CLEANUP(g_backbuffer_rtv); CLEANUP(g_backbuffer); RECT rc; GetClientRect(hWnd, &rc); NOFAIL(g_swpch->lpVtbl->ResizeBuffers(g_swpch, 1, rc.right - rc.left, rc.bottom - rc.top, DXGI_FORMAT_UNKNOWN, 0)); NOFAIL(g_swpch->lpVtbl->GetBuffer(g_swpch, 0, &IID_ID3D11Texture2D, (void**)&g_backbuffer)); NOFAIL(g_dev->lpVtbl->CreateRenderTargetView(g_dev, g_backbuffer, NULL, &g_backbuffer_rtv)); } return 0; } LRESULT MainWnd_OnPaint(HWND hWnd) { // GDI例行任务 PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); // DirectX任务 // 清除背景 float backcolor[] = { 0.0f, 1.0f, 1.0f, 1.0f }; g_immctx->lpVtbl->ClearRenderTargetView(g_immctx, g_backbuffer_rtv, backcolor); // 设置状态并绘图 RECT rc; GetClientRect(hWnd, &rc); float ratio = (float)(rc.right - rc.left) / (float)(rc.bottom - rc.top); float vertexdata[] = { -1.0f, -1.0f, 1.0f, -2.2f * ratio, -2.2f, -1.0f, 1.0f, 1.0f, -2.2f * ratio, 2.2f, 1.0f, -1.0f, 1.0f, 2.2f * ratio, -2.2f, 1.0f, 1.0f, 1.0f, 2.2f * ratio, 2.2f, }; g_immctx->lpVtbl->UpdateSubresource(g_immctx, (ID3D11Resource *)g_vertexbuffer, 0, NULL, vertexdata, 0, 0); UINT stride = sizeof(float) * 5, offset = 0; g_immctx->lpVtbl->IASetVertexBuffers(g_immctx, 0, 1, &g_vertexbuffer, &stride, &offset); g_immctx->lpVtbl->IASetInputLayout(g_immctx, g_inlayout); g_immctx->lpVtbl->IASetPrimitiveTopology(g_immctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); g_immctx->lpVtbl->VSSetShader(g_immctx, g_vs_common, NULL, 0); D3D11_VIEWPORT viewport = { 0.0f, 0.0f, rc.right - rc.left, rc.bottom - rc.top, 0.0f, 1.0f }; g_immctx->lpVtbl->RSSetViewports(g_immctx, 1, &viewport); g_immctx->lpVtbl->PSSetShader(g_immctx, g_ps_current, NULL, 0); g_immctx->lpVtbl->OMSetRenderTargets(g_immctx, 1, &g_backbuffer_rtv, NULL); g_immctx->lpVtbl->Draw(g_immctx, 4, 0); // 上屏 NOFAIL(g_swpch->lpVtbl->Present(g_swpch, 0, 0)); return 0; } LRESULT MainWnd_OnLButtonDown(HWND hWnd, WPARAM wParam, POINTS pts) { g_ps_current = g_ps_mandelbrot; InvalidateRect(hWnd, NULL, FALSE); return 0; } LRESULT MainWnd_OnRButtonDown(HWND hWnd, WPARAM wParam, POINTS pts) { g_ps_current = g_ps_julia; InvalidateRect(hWnd, NULL, FALSE); return 0; } LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: return MainWnd_OnCreate(hWnd, (LPCREATESTRUCT)lParam); case WM_DESTROY: return MainWnd_OnDestroy(hWnd); case WM_SIZE: return MainWnd_OnSize(hWnd, wParam, MAKEPOINTS(lParam)); case WM_LBUTTONDOWN: return MainWnd_OnLButtonDown(hWnd, wParam, MAKEPOINTS(lParam)); case WM_RBUTTONDOWN: return MainWnd_OnRButtonDown(hWnd, wParam, MAKEPOINTS(lParam)); case WM_PAINT: return MainWnd_OnPaint(hWnd); default: return DefWindowProc(hWnd, msg, wParam, lParam); } } int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR szCmdLine, int nShowCmd) { WNDCLASSEX wcex = { sizeof wcex, CS_VREDRAW|CS_HREDRAW, WndProc, 0, 0, g_hinst = hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), NULL/*空画刷防闪烁*/, NULL, L"MainWndClass", LoadIcon(NULL, IDI_APPLICATION) }; RegisterClassEx(&wcex); RECT rc = { 0, 0, 480, 480 }; AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); g_hmainwnd = CreateWindowEx(0, wcex.lpszClassName, L"D3D11主窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL); ShowWindow(g_hmainwnd, nShowCmd); UpdateWindow(g_hmainwnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }


在Windows API中只开放了两个允许区分大小写的API: CreateFile,使用FILE_FLAG_POSIX_SEMANTICS开关; FindFirstFileEx,使用FIND_FIRST_EX_CASE_SENSITIVE开关。 不过我们可以通过SetFileInformationByHandle来实现文件/文件夹的改名和删除。 此外,在Native API中InitializeObjectAttributes时不指定OBJ_CASE_INSENSITIVE开关,也可以实现区分大小写的功能。 所有的这些开关只有注册表键值obcaseinsensitive设为DWORD:0并且【重新启动】之后才有效。obcaseinsensitive的具体位置是HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel。 这个键值只影响Windows API(kernel32.dll)和Native API(ntdll.dll)的行为,Windows Subsystem for Linux是通过未公开的内核调用来实现的,不受这个注册表键值影响。 // ConsoleApplication1.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> #include <stdio.h> int main() { HKEY hsubkey; DWORD valtype, obcaseinsensitive, dwret; LSTATUS lstatus; ULONGLONG renbuf1[64], renbuf2[64]; FILE_RENAME_INFO *ren1, *ren2; FILE_DISPOSITION_INFO del1, del2; HANDLE hdir1, hdir2; HANDLE hfind; WIN32_FIND_DATA fd; char ansipath[MAX_PATH]; // 检测系统是否打开了Win32的大小写敏感接口 hsubkey = NULL; RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel"), &hsubkey); valtype = obcaseinsensitive = 0; dwret = sizeof obcaseinsensitive; lstatus = RegQueryValueEx(hsubkey, TEXT("obcaseinsensitive"), NULL, &valtype, (LPBYTE)&obcaseinsensitive, &dwret); RegCloseKey(hsubkey); if (lstatus != ERROR_SUCCESS || valtype != REG_DWORD || dwret != sizeof obcaseinsensitive || obcaseinsensitive != 0) { printf("HKEY_LOCAL_MACHINE\\CurrentControlSet\\Control\\Session Manager\\kernel\\obcaseinsensitive" " must be set to REG_DWORD:0, and you must restart your computer before trying again.\n"); return 1; } // 创建两个文件夹 CreateDirectory(TEXT("G:\\CaseDir_1"), NULL); CreateDirectory(TEXT("G:\\casedir_123"), NULL); // 改成只有大小写不同的形式 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir_1"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf1, 0, sizeof renbuf1); ren1 = (FILE_RENAME_INFO*)renbuf1; ren1->ReplaceIfExists = FALSE; ren1->RootDirectory = NULL; wcscpy(ren1->FileName, L"G:\\CaseDir"); ren1->FileNameLength = wcslen(ren1->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir1, FileRenameInfo, &renbuf1, sizeof renbuf1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir_123"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf2, 0, sizeof renbuf2); ren2 = (FILE_RENAME_INFO*)renbuf2; ren2->ReplaceIfExists = FALSE; ren2->RootDirectory = NULL; wcscpy(ren2->FileName, L"G:\\casedir"); ren2->FileNameLength = wcslen(ren2->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir2, FileRenameInfo, &renbuf2, sizeof renbuf2); CloseHandle(hdir2); // 按照区分大小写的方式查找文件/文件夹 hfind = FindFirstFileEx(TEXT("G:\\Case*"), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE); if (hfind != INVALID_HANDLE_VALUE) { do { #ifdef UNICODE WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ansipath, MAX_PATH, NULL, NULL); #else strcpy(ansipath, fd.cFileName); #endif printf("%s\n", ansipath); } while (FindNextFile(hfind, &fd)); FindClose(hfind); } #if 0 // 改回来 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf1, 0, sizeof renbuf1); ren1 = (FILE_RENAME_INFO*)renbuf1; ren1->ReplaceIfExists = FALSE; ren1->RootDirectory = NULL; wcscpy(ren1->FileName, L"G:\\CaseDir_1"); ren1->FileNameLength = wcslen(ren1->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir1, FileRenameInfo, &renbuf1, sizeof renbuf1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf2, 0, sizeof renbuf2); ren2 = (FILE_RENAME_INFO*)renbuf2; ren2->ReplaceIfExists = FALSE; ren2->RootDirectory = NULL; wcscpy(ren2->FileName, L"G:\\casedir_123"); ren2->FileNameLength = wcslen(ren2->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir2, FileRenameInfo, &renbuf2, sizeof renbuf2); CloseHandle(hdir2); #else // 删掉 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); del1.DeleteFile = TRUE; SetFileInformationByHandle(hdir1, FileDispositionInfo, &del1, sizeof del1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); del2.DeleteFile = TRUE; SetFileInformationByHandle(hdir2, FileDispositionInfo, &del2, sizeof del2); CloseHandle(hdir2); #endif return 0; }

在Windows API中只开放了两个允许区分大小写的API: CreateFile,使用FILE_FLAG_POSIX_SEMANTICS开关; FindFirstFileEx,使用FIND_FIRST_EX_CASE_SENSITIVE开关。 不过我们可以通过SetFileInformationByHandle来实现文件/文件夹的改名和删除。 此外,在Native API中InitializeObjectAttributes时不指定OBJ_CASE_INSENSITIVE开关,也可以实现区分大小写的功能。 所有的这些开关只有注册表键值obcaseinsensitive设为DWORD:0并且【重新启动】之后才有效。obcaseinsensitive的具体位置是HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel。 这个键值只影响Windows API(kernel32.dll)和Native API(ntdll.dll)的行为,Windows Subsystem for Linux是通过未公开的内核调用来实现的,不受这个注册表键值影响。 (附件:278050) // ConsoleApplication1.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> #include <stdio.h> int main() { HKEY hsubkey; DWORD valtype, obcaseinsensitive, dwret; LSTATUS lstatus; ULONGLONG renbuf1[64], renbuf2[64]; FILE_RENAME_INFO *ren1, *ren2; FILE_DISPOSITION_INFO del1, del2; HANDLE hdir1, hdir2; HANDLE hfind; WIN32_FIND_DATA fd; char ansipath[MAX_PATH]; // 检测系统是否打开了Win32的大小写敏感接口 hsubkey = NULL; RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel"), &hsubkey); valtype = obcaseinsensitive = 0; dwret = sizeof obcaseinsensitive; lstatus = RegQueryValueEx(hsubkey, TEXT("obcaseinsensitive"), NULL, &valtype, (LPBYTE)&obcaseinsensitive, &dwret); RegCloseKey(hsubkey); if (lstatus != ERROR_SUCCESS || valtype != REG_DWORD || dwret != sizeof obcaseinsensitive || obcaseinsensitive != 0) { printf("HKEY_LOCAL_MACHINE\\CurrentControlSet\\Control\\Session Manager\\kernel\\obcaseinsensitive" " must be set to REG_DWORD:0, and you must restart your computer before trying again.\n"); return 1; } // 创建两个文件夹 CreateDirectory(TEXT("G:\\CaseDir_1"), NULL); CreateDirectory(TEXT("G:\\casedir_123"), NULL); // 改成只有大小写不同的形式 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir_1"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf1, 0, sizeof renbuf1); ren1 = (FILE_RENAME_INFO*)renbuf1; ren1->ReplaceIfExists = FALSE; ren1->RootDirectory = NULL; wcscpy(ren1->FileName, L"G:\\CaseDir"); ren1->FileNameLength = wcslen(ren1->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir1, FileRenameInfo, &renbuf1, sizeof renbuf1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir_123"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf2, 0, sizeof renbuf2); ren2 = (FILE_RENAME_INFO*)renbuf2; ren2->ReplaceIfExists = FALSE; ren2->RootDirectory = NULL; wcscpy(ren2->FileName, L"G:\\casedir"); ren2->FileNameLength = wcslen(ren2->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir2, FileRenameInfo, &renbuf2, sizeof renbuf2); CloseHandle(hdir2); // 按照区分大小写的方式查找文件/文件夹 hfind = FindFirstFileEx(TEXT("G:\\Case*"), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE); if (hfind != INVALID_HANDLE_VALUE) { do { #ifdef UNICODE WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ansipath, MAX_PATH, NULL, NULL); #else strcpy(ansipath, fd.cFileName); #endif printf("%s\n", ansipath); } while (FindNextFile(hfind, &fd)); FindClose(hfind); } #if 0 // 改回来 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf1, 0, sizeof renbuf1); ren1 = (FILE_RENAME_INFO*)renbuf1; ren1->ReplaceIfExists = FALSE; ren1->RootDirectory = NULL; wcscpy(ren1->FileName, L"G:\\CaseDir_1"); ren1->FileNameLength = wcslen(ren1->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir1, FileRenameInfo, &renbuf1, sizeof renbuf1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); memset(renbuf2, 0, sizeof renbuf2); ren2 = (FILE_RENAME_INFO*)renbuf2; ren2->ReplaceIfExists = FALSE; ren2->RootDirectory = NULL; wcscpy(ren2->FileName, L"G:\\casedir_123"); ren2->FileNameLength = wcslen(ren2->FileName) * sizeof(wchar_t); SetFileInformationByHandle(hdir2, FileRenameInfo, &renbuf2, sizeof renbuf2); CloseHandle(hdir2); #else // 删掉 // 第一个 hdir1 = CreateFile(TEXT("G:\\CaseDir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); del1.DeleteFile = TRUE; SetFileInformationByHandle(hdir1, FileDispositionInfo, &del1, sizeof del1); CloseHandle(hdir1); // 第二个 hdir2 = CreateFile(TEXT("G:\\casedir"), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL); del2.DeleteFile = TRUE; SetFileInformationByHandle(hdir2, FileDispositionInfo, &del2, sizeof del2); CloseHandle(hdir2); #endif return 0; }


本来打算用Shader Model 2.0,但是经过测试,Shader Model 2.0的功能太原始,连for循环都无法支持,最后只能改用Shader Model 3.0。由于使用了HLSL编译Shader Model 3.0,所以需要VS2002-2010和DirectX 9.0c SDK 2004-2010, 不能使用VC6和DirectX 9.0b SDK。 DX是DirectX的缩写,SM是Shader Model的缩写。 如果改写成DirectX 10/11版本的话,最低应该需要D3D_FEATURE_LEVEL_9_3和ps_4_0_level_9_3,同时还需要编写一个简易的vs_4_0_level_9_3,因为vertex shader和pixel shader在10/11中都不是可选的。 但是由于vs_4_0_level_9_3和ps_4_0_level_9_3不支持通用循环,需要把循环展开成多次条件判断,512的指令数很容易就用超了,对于较复杂的运算,最低建议使用D3D_FEATURE_LEVEL_10_0、vs_4_0和ps_4_0。 vs_1_1 - 128指令 vs_2_0 - 256指令 vs_4_0_level_9_1 - 256指令 vs_2_x - 256指令 vs_4_0_level_9_3 - 256指令 vs_3_0 - 最少512指令,上限到D3DCAPS9.MaxVertexShader30InstructionSlots,支持通用循环 vs_4_0 - 无限制,支持通用循环 ps_2_0 - 32纹理指令+64算术指令 ps_4_0_level_9_1 - 32纹理指令+64算术指令 ps_2_x - 最少96指令,上限到D3DCAPS9.D3DPSHADERCAPS2_0.NumInstructionSlots ps_4_0_level_9_3 - 512指令 vs_3_0 - 最少512指令,上限到D3DCAPS9.MaxPixelShader30InstructionSlots,支持通用循环 vs_4_0 - 无限制,支持通用循环 dxmandelbrot.zip 14.9M 7次 GPU部分: // complexsets.txt float4 ps_mandelbrot(float2 tex : TEXCOORD0) : COLOR0 { float2 zvar = float2(0, 0); float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + tex.x; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + tex.y; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } float4 ps_julia(float2 tex : TEXCOORD0) : COLOR0 { float2 zvar = tex; float i; for (i = 0; i < 16 && zvar.x * zvar.x + zvar.y * zvar.y <= 4; i++) { float2 zvar2 = zvar; zvar.x = zvar2.x * zvar2.x - zvar2.y * zvar2.y + 0.4f; zvar.y = zvar2.x * zvar2.y + zvar2.y * zvar2.x + 0.3f; } return float4(i / 16.0, 0.0f, 0.0f, 1.0f); } CPU部分: // main.c #include <Windows.h> #include <tchar.h> // DirectX 9.0c SDK or higher #include <d3d9.h> #include <d3dx9.h> #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") #pragma comment(linker, "/nodefaultlib:libcp") LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; LPDIRECT3DPIXELSHADER9 g_pPSmandelbrot = NULL; LPDIRECT3DPIXELSHADER9 g_pPSjulia = NULL; LPDIRECT3DPIXELSHADER9 g_pPS = NULL; typedef struct CUSTOMVERTEX { FLOAT x, y, z, rhw; FLOAT u, v; }CUSTOMVERTEX; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1) HRESULT InitD3D(HWND hWnd) { D3DPRESENT_PARAMETERS d3dpp; if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; if (FAILED(IDirect3D9_CreateDevice(g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice))) { return E_FAIL; } return S_OK; } HRESULT CompileAndCreatePS(LPCTSTR filename, LPSTR funcname, LPDIRECT3DPIXELSHADER9 *pps) { LPD3DXBUFFER psbuf = NULL, errmsg = NULL; HRESULT hr = S_OK; hr = D3DXCompileShaderFromFile(filename, NULL, NULL, funcname, "ps_3_0", 0, &psbuf, &errmsg, NULL); if (errmsg) { MessageBoxA(NULL, (char*)errmsg->lpVtbl->GetBufferPointer(errmsg), "err", MB_ICONERROR); errmsg->lpVtbl->Release(errmsg); return hr; } else if (FAILED(hr)) { return hr; } hr = IDirect3DDevice9_CreatePixelShader(g_pd3dDevice, (DWORD*)psbuf->lpVtbl->GetBufferPointer(psbuf), pps); psbuf->lpVtbl->Release(psbuf); return S_OK; } HRESULT InitRes() { CUSTOMVERTEX vertices[] = { { 0.0f, 0.0f, 0.5f, 1.0f, -2.2f, -2.2f, }, { 480.0f, 0.0f, 0.5f, 1.0f, 2.2f, -2.2f, }, { 0.0f, 480.0f, 0.5f, 1.0f, -2.2f, 2.2f, }, { 480.0f, 480.0f, 0.5f, 1.0f, 2.2f, 2.2f, }, }; HRESULT hr = S_OK; VOID* pVertices; hr = IDirect3DDevice9_CreateVertexBuffer(g_pd3dDevice, 4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL); if (FAILED(hr)) { return hr; } hr = IDirect3DVertexBuffer9_Lock(g_pVB, 0, sizeof(vertices), (void**)&pVertices, 0); if (FAILED(hr)) return hr; memcpy(pVertices, vertices, sizeof(vertices)); IDirect3DVertexBuffer9_Unlock(g_pVB); CompileAndCreatePS(_T("complexsets.txt"), "ps_mandelbrot", &g_pPSmandelbrot); CompileAndCreatePS(_T("complexsets.txt"), "ps_julia", &g_pPSjulia); g_pPS = g_pPSmandelbrot; return S_OK; } VOID Cleanup() { if (g_pPSjulia) IDirect3DPixelShader9_Release(g_pPSjulia); if (g_pPSmandelbrot) IDirect3DPixelShader9_Release(g_pPSmandelbrot); if (g_pVB != NULL) IDirect3DVertexBuffer9_Release(g_pVB); if (g_pd3dDevice != NULL) IDirect3DDevice9_Release(g_pd3dDevice); if (g_pD3D != NULL) IDirect3D9_Release(g_pD3D); } VOID Render() { IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0); if (SUCCEEDED(IDirect3DDevice9_BeginScene(g_pd3dDevice))) { IDirect3DDevice9_SetStreamSource(g_pd3dDevice, 0, g_pVB, 0, sizeof(CUSTOMVERTEX)); IDirect3DDevice9_SetFVF(g_pd3dDevice, D3DFVF_CUSTOMVERTEX); IDirect3DDevice9_SetPixelShader(g_pd3dDevice, g_pPS); IDirect3DDevice9_DrawPrimitive(g_pd3dDevice, D3DPT_TRIANGLESTRIP, 0, 2); IDirect3DDevice9_EndScene(g_pd3dDevice); } IDirect3DDevice9_Present(g_pd3dDevice, NULL, NULL, NULL, NULL); } LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_LBUTTONDOWN: g_pPS = g_pPSmandelbrot; return 0; case WM_RBUTTONDOWN: g_pPS = g_pPSjulia; return 0; case WM_DESTROY: Cleanup(); PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); } INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nShowCmd) { WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, hInst, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), NULL, NULL, _T("MainWndProc"), wc.hIcon }; RECT rc = { 0, 0, 480, 480 }; MSG msg; HWND hWnd; if (!RegisterClassEx(&wc)) return 0; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); hWnd = CreateWindow(wc.lpszClassName, _T("Mandelbrot and Julia"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, wc.hInstance, NULL); if (SUCCEEDED(InitD3D(hWnd))) { if (SUCCEEDED(InitRes())) { ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { Render(); } } } } return 0; } 更新:支持Visual C++ 6.0和DirectX 9.0b SDK 通过DirectX 9.0c SDK的fxc将上述HLSL代码编译为Shader Model 3.0 ASM代码,即可支持DirectX 9.0b SDK。 ps_mandelbrot.txt: // ps_mandelbrot.txt ps_3_0 def c0, 0, 0, 4, 1 def c1, 0.0625, 0, 0, 0 defi i0, 16, 0, 0, 0 dcl_texcoord v0.xy mov r0.y, c0.y mov r0.z, c0.y mov r0.w, c0.y rep i0 mul r0.x, r0.z, r0.z mad r0.x, r0.y, r0.y, r0.x break_lt c0.z, r0.x mul r0.x, r0.z, r0.z mad r0.x, r0.y, r0.y, -r0.x add r0.x, r0.x, v0.x dp2add r0.z, r0.y, r0.z, v0.y add r0.w, r0.w, c0.w mov r0.y, r0.x endrep mul oC0.x, r0.w, c1.x mov oC0.yzw, c0.xyyw ps_julia.txt // ps_julia.txt ps_3_0 def c0, 0, 0, 4, 0.400000006 def c1, 0.300000012, 1, 0.0625, 0 defi i0, 16, 0, 0, 0 dcl_texcoord v0.xy mov r0.y, v0.x mov r0.z, v0.y mov r0.w, c0.y rep i0 mul r0.x, r0.z, r0.z mad r0.x, r0.y, r0.y, r0.x break_lt c0.z, r0.x mul r0.x, r0.z, r0.z mad r0.x, r0.y, r0.y, -r0.x add r0.x, r0.x, c0.w dp2add r0.z, r0.y, r0.z, c1.x add r0.w, r0.w, c1.y mov r0.y, r0.x endrep mul oC0.x, r0.w, c1.z mov oC0.yzw, c1.xwwy main.c // main.c #include <Windows.h> #include <tchar.h> #include <d3d9.h> #include <d3dx9.h> #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; LPDIRECT3DPIXELSHADER9 g_pPSmandelbrot = NULL; LPDIRECT3DPIXELSHADER9 g_pPSjulia = NULL; LPDIRECT3DPIXELSHADER9 g_pPS = NULL; typedef struct CUSTOMVERTEX { FLOAT x, y, z, rhw; FLOAT u, v; }CUSTOMVERTEX; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1) HRESULT InitD3D(HWND hWnd) { D3DPRESENT_PARAMETERS d3dpp; if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; if (FAILED(IDirect3D9_CreateDevice(g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice))) { return E_FAIL; } return S_OK; } HRESULT AssembleAndCreatePS(LPCTSTR filename, LPDIRECT3DPIXELSHADER9 *pps) { LPD3DXBUFFER psbuf = NULL, errmsg = NULL; HRESULT hr = S_OK; hr = D3DXAssembleShaderFromFile(filename, NULL, NULL, 0, &psbuf, &errmsg); if (errmsg) { MessageBoxA(NULL, (char*)errmsg->lpVtbl->GetBufferPointer(errmsg), "err", MB_ICONERROR); errmsg->lpVtbl->Release(errmsg); return hr; } else if (FAILED(hr)) { return hr; } hr = IDirect3DDevice9_CreatePixelShader(g_pd3dDevice, (DWORD*)psbuf->lpVtbl->GetBufferPointer(psbuf), pps); psbuf->lpVtbl->Release(psbuf); return S_OK; } HRESULT InitRes() { CUSTOMVERTEX vertices[] = { { 0.0f, 0.0f, 0.5f, 1.0f, -2.2f, -2.2f, }, { 480.0f, 0.0f, 0.5f, 1.0f, 2.2f, -2.2f, }, { 0.0f, 480.0f, 0.5f, 1.0f, -2.2f, 2.2f, }, { 480.0f, 480.0f, 0.5f, 1.0f, 2.2f, 2.2f, }, }; HRESULT hr = S_OK; VOID* pVertices; hr = IDirect3DDevice9_CreateVertexBuffer(g_pd3dDevice, 4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL); if (FAILED(hr)) { return hr; } hr = IDirect3DVertexBuffer9_Lock(g_pVB, 0, sizeof(vertices), (void**)&pVertices, 0); if (FAILED(hr)) return hr; memcpy(pVertices, vertices, sizeof(vertices)); IDirect3DVertexBuffer9_Unlock(g_pVB); AssembleAndCreatePS(_T("ps_mandelbrot.txt"), &g_pPSmandelbrot); AssembleAndCreatePS(_T("ps_julia.txt"), &g_pPSjulia); g_pPS = g_pPSmandelbrot; return S_OK; } VOID Cleanup() { if (g_pPSjulia) IDirect3DPixelShader9_Release(g_pPSjulia); if (g_pPSmandelbrot) IDirect3DPixelShader9_Release(g_pPSmandelbrot); if (g_pVB != NULL) IDirect3DVertexBuffer9_Release(g_pVB); if (g_pd3dDevice != NULL) IDirect3DDevice9_Release(g_pd3dDevice); if (g_pD3D != NULL) IDirect3D9_Release(g_pD3D); } VOID Render() { IDirect3DDevice9_Clear(g_pd3dDevice, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0); if (SUCCEEDED(IDirect3DDevice9_BeginScene(g_pd3dDevice))) { IDirect3DDevice9_SetStreamSource(g_pd3dDevice, 0, g_pVB, 0, sizeof(CUSTOMVERTEX)); IDirect3DDevice9_SetFVF(g_pd3dDevice, D3DFVF_CUSTOMVERTEX); IDirect3DDevice9_SetPixelShader(g_pd3dDevice, g_pPS); IDirect3DDevice9_DrawPrimitive(g_pd3dDevice, D3DPT_TRIANGLESTRIP, 0, 2); IDirect3DDevice9_EndScene(g_pd3dDevice); } IDirect3DDevice9_Present(g_pd3dDevice, NULL, NULL, NULL, NULL); } LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_LBUTTONDOWN: g_pPS = g_pPSmandelbrot; return 0; case WM_RBUTTONDOWN: g_pPS = g_pPSjulia; return 0; case WM_DESTROY: Cleanup(); PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); } INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nShowCmd) { WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, hInst, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), NULL, NULL, _T("MainWndProc"), wc.hIcon }; RECT rc = { 0, 0, 480, 480 }; MSG msg; HWND hWnd; if (!RegisterClassEx(&wc)) return 0; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); hWnd = CreateWindow(wc.lpszClassName, _T("Mandelbrot and Julia"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, wc.hInstance, NULL); if (SUCCEEDED(InitD3D(hWnd))) { if (SUCCEEDED(InitRes())) { ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { Render(); } } } } return 0; }

写了个cuda版的 #include <stdio.h> #include <stdlib.h> #include <cuda_runtime.h> #define grid 10 #define block 1024 bool InitCUDA() { int count; cudaGetDeviceCount(&count); if (count == 0) { fprintf(stderr, "There is no device.\n"); return false; } int i; for (i = 0; i < count; i++) { cudaDeviceProp prop; cudaGetDeviceProperties(&prop, i); if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) { if (prop.major >= 1) { break; } } } if (i == count) { fprintf(stderr, "There is no device supporting CUDA 1.x.\n"); return false; } cudaSetDevice(i); return true; } struct complex{ float r; float i; }; __device__ float mod(struct complex a){ return sqrt(a.r*a.r+a.i*a.i); } __device__ struct complex add(struct complex a,struct complex b){ struct complex c; c.r=a.r+b.r; c.i=a.i+b.i; return c; } __device__ struct complex square(struct complex a){ struct complex b; b.r=a.r*a.r-a.i*a.i; b.i=2*a.r*a.i; return b; } #define maxx 1000 __global__ void man(struct complex *p,int *o,int tol,int pixels) { int id=blockIdx.x*blockDim.x+threadIdx.x; int i,j,k; struct complex z,c; for(i=0;i*tol+id<pixels;i++){ z.r=0; z.i=0; c=p[i*tol+id]; k=maxx; for(j=1;j<maxx;j++){ z=add(square(z),c); if((k==maxx)&&(mod(z)>2.0f)){ k=j; } } o[i*tol+id]=k; } } #define height 2000 #define width 3000 #define xstart -2.0f #define xend 1.0f #define ystart -1.0f #define yend 1.0f int main() { if (!InitCUDA()) { return 0; } struct complex* pic=(struct complex*)malloc(sizeof(struct complex)*width*height); int i,j; for(i=0;i<width;i++){ for(j=0;j<height;j++){ pic[i*height+j].r=xstart+(xend-xstart)*i/width; pic[i*height+j].i=ystart+(yend-ystart)*j/height; } } struct complex* picd; int *o=(int*)malloc(sizeof(int)*width*height); int *od; cudaMalloc(&picd,sizeof(struct complex)*width*height); cudaMemcpy(picd,pic,sizeof(struct complex)*width*height,cudaMemcpyHostToDevice); cudaMalloc(&od,sizeof(int)*width*height); man<<<grid,block>>>(picd,od,grid*block,height*width); cudaMemcpy(o,od,sizeof(int)*width*height,cudaMemcpyDeviceToHost); FILE *fp=fopen("./out.ppm","w"); fprintf(fp,"P3\n%d %d\n255\n",width,height); int r,g,b,col; for(i=0;i<height;i++){ for(j=0;j<width;j++){ col=o[j*height+i]; if(col==maxx){ r=0; g=0; b=0; }else{ b=(int)max(min((0+(int)(512.0f/sqrt(o[j*height+i]))),255),0); g=0; r=0; } fprintf(fp,"%d %d %d ",r,g,b); } fprintf(fp,"\n"); } fclose(fp); free(pic); free(o); cudaFree(picd); cudaFree(od); cudaDeviceReset(); return 0; } (附件:278048)


《2017/09/13更新》 当前现存Windows 10版本的生命周期政策 现存的桌面SAC版本: Windows 10 (Version 1507) Build 10240 支持到 2017/05/09 Windows 10 Version 1511 Build 10586 支持到 2017/10/10 Windows 10 Version 1607 Build 14393 支持到 暂定2018/03 (Clover Trail 支持到 2023/01) Windows 10 Version 1703 Build 15063 支持到 暂定2018/09 现存的桌面LTSC版本: Windows 10 Enterprise 2015 LTSB 主流支持到 2020/10/13 延伸支持到 2025/10/14 Windows 10 Enterprise 2016 LTSB 主流支持到 2021/10/12 延伸支持到 2026/10/13 现存的服务器LTSC版本: Windows Server 2016 主流支持到 2022/01/11 延伸支持到 2027/01/11 参考资料: https://support.microsoft.com/zh-cn/help/13853/windows-lifecycle-fact-sheet https://support.microsoft.com/zh-cn/lifecycle https://docs.microsoft.com/en-us/windows/deployment/update/waas-overview https://blogs.technet.microsoft.com/windowsserver/2017/08/24/sneak-peek-1-windows-server-version-1709/ https://answers.microsoft.com/en-us/windows/forum/windows_10-windows_install/intel-clover-trail-processors-are-not-supported-on/ed1823d3-c82c-4d7f-ba9d-43ecbcf526e9



时间在流逝,世界在变化。对于编程工具的使用,激进派当然好选择了,尽量用最新的(比如vs2017),这样即使稍有过时也不会立刻落伍。 但是如果你是保守派,正在坚守一个你认为刚刚好,不会过新也不会过旧的工具,学会了可以立刻致大用,就要当心你所坚守的工具,在坚守了几年之后,可能落伍了。 就说一下C/C++的事情。Win98时代,最流行的是turboc2.0。WinXP时代,再用turboc2.0已经令人抓狂了,自然就要上vc6.0(也可以上devcpp或cfree,但是要写支持中文的gui当然是vc6.0用起来舒服)。2013年左右Win7/Win8/Win8.1登上取代WinXP的征程的时候,vc6.0体验很差,当然要用vs2005/2008/2010(同样的,也有人用codeblocks)。 现在2017年,WinXP已经渐渐淡出历史,Win7成了新时代的WinXP,Win10正在半年一次飞速更新,用什么版本的vs最好呢? 如果不想折腾的话,VS2012(Update5)加上最新版的.NET DevPack能够满足绝大多数爱好者的要求。 为什么不用VS2013/2015呢?VS2013/2015很多功能需要装IE10/11,装了IE10/11就得装上平台更新KB2670838,但是我见过的盗版Win7大部分是IE8/9,自然也是没有KB2670838的。简而言之,Win7SP1装VS2012没有先决条件,是使用VS2012的最大理由。 为什么不用VS2005/2008/2010呢?这是因为VS2012支持比较新的技术,比以前的版本好用很多。 对于C#/VB爱好者,它支持最新的.NET4.x(只要安装最新的.NET DevPack),虽然语法只有C#/VB 5.0,但也够用了。对于数据库,它原生支持超轻量级的SQLLocalDB,不用再担心SQLExpress在新系统的兼容性问题。目前来看比较好用。 对于C++爱好者,它自带DirectX编译器d3dcompiler_46.dll,同时,它也开始支持了C++AMP这种简化调用GPU运算的技术,它也对C++/CLI恢复了智能提示支持。目前来看比较好用。 要注意的是,这篇文章只是说「在2017年,vs2012是比较好用的版本」,并不表明以后仍然好用,甚至不保证2018年是比较好用的版本。究竟是放弃使用保守版本去追新,还是继续寻找新的保守版本,就要看你自己的判断力了。 VS2012下载地址: VS2012Ultimate简体中文(已集成序列号): ed2k://|file|cn_visual_studio_ultimate_2012_x86_dvd_920954.iso|1643802624|A3058CE4F2AD7035DB6E67586D07AAA5|/ VS2012Ultimate英文(已集成序列号): ed2k://|file|en_visual_studio_ultimate_2012_x86_dvd_920947.iso|1594998784|1BBECB1C7A892DA7D97EC4C840953915|/ Update5升级包: ed2k://|file|mu_visual_studio_2012_update_5_x86_dvd_6967467.iso|2538768384|405A484D70CA922DC6FC1F204818D412|/ .NET DevPack下载地址: 最新版下载页面: https://www.microsoft.com/net/targeting .NET4.6.2 DevPack: ed2k://|file|en_.net_fx_4_6_2_dp_lps_win_7sp1_8dot1_10_win_server_2008sp2_2008r2sp1_2012_2012r2_x86_x64_9058145.exe|86788848|064175480A208A61C4E68CB678A399B3|/ .NET4.6.2 DevPack 简体中文语言包: ed2k://|file|cn_.net_fx_4_6_2_dp_lps_win_7sp1_8dot1_10_win_server_2008sp2_2008r2sp1_2012_2012r2_x86_x64_9058141.exe|13237608|D4E1F9DA83CAD505E89B43FF194FBFDB|/ Win8WDK暂时还没有离线的解决方案,但是也不是每一个人都要去学习编写驱动的。 其它资源: DirectXTK for DirectX 11 [2014-09]: DirectXTK-sept2014.zip 583k 对于Office编程,Visual Studio 2012只支持Office 2010,不支持较早的Office 2007和较晚的Office 2013,这是比较坑的一点。各版本支持的如下: Visual Studio 2008支持Office 2003/2007 Visual Studio 2010支持Office 2007/2010 Visual Studio 2012支持Office 2010 Visual Studio 2013支持Office 2010/2013 Visual Studio 2015/2017支持Office 2010/2013/2016


不违背语言逻辑的做法都是可行的。。。 像是这种情况,就不能用goto。 #include <Windows.h> #include <tchar.h> #include <string> #include <iostream> using namespace std; int _tmain(int argc, TCHAR **argv) { if (argc >= 1) goto b; int i = 1; // warning & assert fail //string s = "aaa"; // compile error b: cout << i << endl; return 0; } 微软那个可以用goto是因为变量声明和初始化都写在函数开头附近,不存在被跳过的问题。 一般来说,如果你习惯于C89风格的那种变量声明全部写在开头的做法,完全可以随意使用goto。但是如果你习惯于ISO C++风格的那种变量声明写在程序中间的做法,就应该考虑一下其它写法。比如你如果觉得do-while(0)-break太拽,完全可以换一种写法。 需要清理的资源变量声明; { 不需要清理的变量声明; 可能失败的操作; if (失败) goto 清理; // ... } 清理: if (变量1需要清理) 清理变量1; // ...



nkc Development Server  https://github.com/kccd/nkc

科创研究院 (c)2005-2016

蜀ICP备11004945号-2 川公网安备51010802000058号