嗨玩手游网

我的世界

我的世界

休闲益智|854.7MB

我的世界国际免费游戏是一...

下载

幻御云丨我的世界最全指令大全

/kill @e[type = evocation_illager] 清除卫道士

/gamemode 0 生存

/gamemode 1 创造

/gamemode 2 冒险

/gamemode 3 旁观 (旧版本不能使用)

/gamerule keepInventory true 死亡不掉落指令

/difficulty 0,1,2,3 分别是和平,简单,普通,困难

/give 玩家名 物品ID 物品数量 给予玩家物品

/weather rain 下雨

/weather clear 无天气

/weather thunder 雷阵雨

/kill 自杀

/spawnpoint 设置重生点

/effect clear 移除身上所有效果

/clear 清除背包所有物品

/scoreboard 计分板

/advancement 更改玩家进度

,/bossbar 更改boss血条

,/clear 清除玩家物品

,/clone 复制方块,

/data 改变方块或实体的NBT数据,

/datapack 管理数据包,

/debug 开始调试,

/defaultgamemode 更改默认游戏模式

/difficulty 设置难度,

/effect 添加或移除药水效果,

/enchant 附魔玩家物品,

/execute 以另一实体身份执行命令,

/experience 刷经验,

/fill 填充方块,

/function 运行函数,

/gamemode 更改游戏模式,

/gamerule 更改游戏规则,

/give 给玩家物品,

/help 显示帮助,

/kill 杀死实体,

/locate 定位最近的建筑坐标,

/me (不知道如何描述),

/particle 生成粒子效果,

/playsound 播放音乐,

/recipe 管理合成,

/reload 重载数据包,

/replaceitem 替换物品

/say 说话,

/scoreboard 记分板,

/seed 显示种子,

/setblock 放置方块,

/setworldspawn 设置出生点

/spawnpoint 设置重生点,

/spreadplayers 随机分散实体,

/stats 探测命令执行结果,

/stopsound 停止音效,

/summon 生成实体,

/tag 修改实体标签,

/team 修改队伍,

/teleport 传送实体(和tp有点区别),

/tell 发私信,

/tellraw 发送高级信息,

/testfor 探测实体

/testforblock 探测方块,

/testforblocks 探测区域内方块是否相同,

/time 更改或查询时间,

/title 显示标题,

/toggledownfall 切换天气,

/tp 传送实体,

/trigger 更改判据为trigger的计分板,

/weather 设置天气,

/worldborder 更改世界边缘,

/xp 刷经验.

/?/help的替代命令,提供命令使用帮助

/ability赋予或剥夺玩家的能力。

/clear从玩家物品栏中删除物品。

/clone将特定区域的方块复制到另一处。

/connect/wsserver的替代命令,连接至WebSocket服务器。

/deop撤销玩家的管理员权限。

/difficulty设置难度等级。

/effect添加或移除状态效果。

/enchant附魔玩家的物品。

/execute执行另一命令。

/experience给予玩家经验。

/fill将某个区域填满特定方块。

/function运行一个函数。

/gamemode更改玩家的游戏模式。

/gamerule更改或查询游戏规则值。

/give给予玩家物品。

/help提供命令使用帮助。

/kill杀死实体(玩家、生物、物品等)。

/list列出服务器中的玩家。

/locate显示最近给定结构的坐标。

/me显示一条关于自己的信息。

/mixerMixer交互性控制。

/mobevent控制或查询允许运行的生物事件。

/msg/tell的替代命令,向另一玩家发送私信。

/op授予玩家管理员权限。

/particle创建颗粒。

/playsound播放音效。

/reload从硬盘中重新加载战利品表、进度和函数。

/replaceitem替换物品栏中的物品。

/resupply立即补给村庄新经济供需体系。

/save准备备份,查询其状态或恢复。

/say向多个玩家发送消息。

/scoreboard管理记分板目标、玩家、队伍与标签。

/setblock将方块替换为其他方块。

/setmaxplayers设置可加入游戏的玩家数量上限。

/setworldspawn设置世界出生点。

/spawnpoint为玩家设置出生点。

/spreadplayers将实体传送到随机位置。

/stopsound 停止音效。

/summon 生成实体。

/tag 修改玩家或实体的标签。

/teleport /tp的替代命令,传送实体。

/tell 向另一玩家发送私信。

/tellraw向玩家显示JSON消息。

/testfor 统计符合给定条件的实体。

/testforblock测定某方块是否在某位置。

/testforblocks 测定两个区域中的方块是否相同。

/time 更改或查询游戏中的世界时间。

/title 管理屏幕上的标题。

/toggledownfall 切换天气。

/tp 传送实体。

/w /tell的替代命令,向另一玩家发送私信。

/weather设置天气。

/wsserver 连接至WebSocket服务器。

/xp 增加或减少经验。

召唤生物:

召唤已驯服的僵尸马:/summon EntityHorse ~ ~ ~ {Type:3,Tame:1}

   召唤未驯服的僵尸马:/summon EntityHorse ~ ~ ~ {Type:3}

 召唤已驯服的骷髅马:/summon EntityHorse ~ ~ ~ {Type:4,Tame:1}

 召唤未驯服的骷髅马:/summon EntityHorse ~ ~ ~ {Type:4}

 召唤僵尸巨人:/summon Giant

/summon Giant 巨型僵尸

/summon EnderDragon 末影龙

/summon WitherBoss 凋零

/summon Skeleton 骷髅

/summon Pig ~ ~ ~ 输入指令可以刷出一只猪

/summon Cow ~ ~ ~ 输入指令可以刷出一只牛

/summon Zombie ~ ~ ~ 输入指令可以刷出一只僵尸

/summon Cow - 牛

/summon Chicken - 鸡

/summon MushroomCow - 蘑菇牛

/summon Bat - 蝙蝠

/summon Pig - 猪

/summon EntityHorse - 马

/summon Sheep - 羊

/summon Villager - 村民

/summon VillagerGolem - 铁傀儡

/summon SnowMan - 雪傀儡

/summon Wolf - 狼/狗

/summon Ozelot - 猫

/summon Squid - 鱿鱼

/summon Zombie - 僵尸

/summon Skeleton - 骷髅

/summon Creeper - 苦力怕/自爆怪/JJ怪

/summon PigZombie - 僵尸猪人

/summon Ghast - 幽灵/恶魂

/summon Enderman - 末影人/“黑基佬”

/summon Silverfish - 银鱼虫/螨虫

/summon Endermite - 末影螨虫

/summon Slime - 史莱姆

/summon LavaSlime - 地狱史莱姆

/summon Witch - 女巫

/summon Guardian - 水下保卫

/summon Blaze - 烈焰人

/summon Spider - 蜘蛛

/summon CaveSpider - 洞穴蜘蛛

/summon EnderDragon - 末影龙Boss

/summon WitherBoss - 凋零Boss

/summon Giant - 巨型僵尸

非生物:

/summon Boat - 船

/summon MinecartRideable - 普通矿车

/summon MinecartHopper - 漏斗矿车

/summon MinecartFurnace - 熔炉矿车

/summon MinecartCommandBlock - 指令方块矿车

/summon MinecartChest - 箱子矿车

/summon MinecartSpawner - 刷怪笼矿车

/summon SmallFireball - 小型火球(烈焰人发出的)

/summon Fireball - 火球(恶魂发出的)

/summon Item - 物品

/summon LeashKnot - 绳子结

/summon Painting - 画

/summon LightningBolt - 雷电

/summon ThrownExpBottle - 已扔出的XP瓶子

/summon WitherSkull - 凋零骷髅头(凋零Boss发出的骷髅头)

/summon EnderCrystal - 末影水晶

/summon FireworksRocketEntity - 已发出的烟火

/summon Arrow - 已射出的箭

/summon ThrownPotion - 已扔出的药

ThrownEnderpearl - 已扔出的末影珍珠

/summon EyeOfEnderSignal - 末影之眼的信号

/summon PrimedTnt - 已点燃的TNT

/summon FallingSand - 掉落沙属性

/summon ItemFrame - 物品显示

/summon XPOrb - 经验球

/summon unknown - 鱼饵

《高能英雄》评测:让\"吃鸡\"这个沉寂已久的赛道又“高能”了起来

《高能英雄》高能在哪?

还没等我来得及从游戏中寻找到答案,海外的反应热度早已为“高能”二字加上了注脚——游戏还未在国内正式上线,在海外的上市计划更是八字没有一撇,却早已声名在外。

在油管、在tik tok、在Twitch(游戏直播平台)、在推特(现在叫叉)……几乎所有歪果仁常用的主流平台,共同回荡着外网玩家们的呼声:“求求了,我们到底要怎样才能玩上这款游戏?”他们甚至跑到B站的官方账号和二手某鱼上向中国网友求助,为了一个试玩账号费尽吃奶的力气。

终于有高手给出了攻略,使用代理、使用翻译器、注册微信、注册QQ……折腾了半天,最终还是被一个中国手机号给拦住了——这一系列的遭遇,不免让我回忆起自己玩韩国游戏时的痛苦回忆。

上线首日便登顶了国区App Store免费榜

在《高能英雄》国内正式上架的当口儿,作为首批能轻松玩上这款游戏的玩家,心里不免还是有些感慨的。不过说真的,这款游戏到底有什么魅力,让来自世界各地的玩家挤破了脑袋都想先试为快呢?

吃鸡品类的再次进化:差异美术题材+“英雄”射击

作为一款以英雄为命名的游戏,《高能英雄》中的英雄群像自然是游戏中的核心亮点之一。除了角色的基本形象塑造,终结动画的演出之外,未来科幻题材的卡渲风和色彩处理都与如今讲究写实风的主流射击品类手游有着明显的美术题材差异。这种新鲜感是一进入游戏便能明显感知到的。

终结动画

但美术的皮囊并不是最主要的,围绕一支队伍的有机组成,高能英雄们在技能定位和功能方面的差异化更为突出:英雄们被划分为突击、侦察和支援三大定位,玩家可以根据自己的喜好和团队战术需求,选择合适的英雄。

例如,身为机器人的开路先驱就以卓越的机动性著称。既能利用钩爪快速位移,又能摆放出一段架空的绳索在上面滑行,可以规避各种地形障碍。在平常的跑毒过程中可以加快团队的移动效率快速进圈,而在决赛圈又能让团队快速脱离战斗,获取地形优势;

治愈之心一眼就是个辅助角色,战术技能是释放屏障并回复生命值,绝招则是召唤空投补给;暗眼擅长于战术侦察,无人机可以探查地图上的敌方单位,还可以切换到无人机视野,针对敌人展开电磁干扰和爆破攻击……并且在《高能英雄》中不同的英雄技能之间可以互相配合、互动,一旦达成默契,就如同产生奇妙的化学反应,可以达到“1+1>2”的效果加成。

比如重装盾卫打开能量护盾,“保护伞”下,虚空之刺趁机隐身偷袭,治愈之心在一旁召唤补给,像极了复仇者联盟的强强联合。在B站上甚至有整活的玩家还研发出自己和自己“叠Buff”的玩法:先给自己整个防御套子,再给自己下个轰炸指令,“伤敌一千、自损0.1”的效果就此达成。

这种技能的叠加拓展了游戏的研究深度,大大拔升了战术竞技游戏的多样化、策略性和可玩性,让战斗过程变得极为精彩而又富有趣味。毕竟过去的战术竞技游戏,玩家个体的能力大部分只能应用在“射击”这一基础行为上,《高能英雄》英雄技能的加入则大大提升了玩家个体能力的上限。

值得注意的是,英雄的技能设计更多偏向功能性而非伤害性,这意味着游戏的平衡性不会因为角色的不同遭到破坏。游戏设计也遵循了战术竞技的基本准则——没有战力特别碾压的强力角色,只有游戏外擅用技能为角色建造优势的大脑。

游戏的武器种类、配件、瞄具选择相当丰富

比起过去的战术竞技游戏,《高能英雄》的战斗节奏显得非常快,游戏里有着不少提升玩家交火频次的策略设计。

最直观的就是游戏的TTK(Time To Kill,即击杀敌人所需的时间)被有意地拉长了,TTK的提升意味着玩家不会轻易被秒杀,有更多的机会实现不同的战术策略,强化了英雄技能对战局的影响;英雄技能的CD相对较快,不会出现攒个大憋一整局不丢的窘状;“落地成盒”干瞪眼的情况也不再发生,在满足一定的条件时可在特定位置复活队友,最大限度地保证了游戏的参与感;更易收集的护甲和血包资源,也让玩家的生存续航得到提升;

这些减少死亡惩罚的设计目的,明显是为了鼓励玩家去高密度地交火,不再让玩家像过往战术竞技游戏中那样,为了活到决赛圈,提升名次而倾向于避战苟活。《高能英雄》则是让玩家在高频次的刚枪和英雄技能所带来的策略博弈中,获得持续的正向反馈。

另外一个值得夸赞的设计是游戏的地图。复杂的地形设计和丰富的高低落差,让地图成为一个肆意挥洒的近未来战场。游戏中设计的各种地形机关,如跳板、风筒、弹簧等,都在无形中驱使玩家移动,把玩家推向更高频的遭遇和战斗。

模式丰富,不拘泥于单一的“吃鸡”玩法

移动端的战术射击该怎么做?

在很长一段时间里,我都认为“射击”,尤其是“战术射击”这一重度品类在移动端上的表现是存疑的。其原因在于相较于其它平台,移动端的战术射击游戏除了需要把游戏底层规则调教好外,还要思考如何尽可能的优化玩家的游戏体验。比如在小屏上尽可能的展示核心信息,简化玩家的操作等等。

我们在玩《高能英雄》时就能发现,游戏创造了不少友好的人性化设计:跳伞时,地图上会显示附近的敌人;在开倍镜射击时,可以直观地从倍镜的LED屏上看到剩余的子弹数;甚至在被敌人瞄准的时候,系统还会温馨提示,连距离都标出来,安全感直接拉满。

除了数字外,也有动态图条,更直观的体现剩余备弹

此外,像战术竞技品类特有的收集物资,也都依照物品的品质差异,武器的需求等做了自动捡拾的处理。游戏的操作自由度也非常高,该有的基础设置、自定义设置和灵敏度调节都有,甚至连跳跃键和蹲下键都有对应的设置可以让玩家转动视角,这一设置意味着在做出滑铲跳、蹬墙跳等动作的时候,操作起来能够非常丝滑。

可调式的选项非常多

得益于上述视觉和操作两个维度上的优化,我们在对抗中需要兼顾的信息和操作行为被大幅简化,体验起来相当舒心。

《高能英雄》的“高能宇宙”

《高能英雄》的英雄不仅有着独特的技能,还有着各自鲜明的个性,共享着一个背景故事。游戏的世界观设定在近未来的科幻背景下,复古斗笠和未来机甲,日式忍者风格和美式摇滚style……

东情西韵,古代未来,文化元素彼此交织、冲撞、融合,不仅诞生了设计上的新意,令角色焕发出魅力和个性,也是不仅为中国玩家所接受、也能吸引到国外玩家的重要因素。在我们看来,角色的高辨识度和“带戏”程度,当游戏有了更广泛的热度以后,衍生出动漫产品等周边、打造一个完整的“高能英雄宇宙”也不是没有可能。

游戏近未来科幻的风格不仅覆盖角色阵容,在游戏的整体设计上亦有所延伸——游戏的UI的几何体设计自带未来质感;武器方面,除了传统的冲锋枪、狙击枪、霰弹枪,能量枪使用能量电池、需要冷却充能,提前将未来引到现在;地图设计中也颇具未来感元素,微型太空舱一样的空投箱、好像在外星球上搭建的建筑物外立面……都有一种带着玩家暂时逃离地球享受片刻的感觉。

结语

经过多年内卷,射击手游已经进化成一个高度成熟的领域,原有的头部作品割据市场版图,筑起壁垒高墙,让后续进场的游戏很难突围出自己的一片天地。

但《高能英雄》的上线让我们眼前一亮。英雄与传术竞技的完美耦合,加之移动端的优秀适配,使得原本玩法相对固化的战术竞技手游品类有了新的变数。尽管上线的表现还需要进一步的验证和观察,笔者认为《高能英雄》是一只能打开传术竞技的市场的潜力黑马,让我们一起期待吧!

Ascend C保姆级教程:我的第一份Ascend C代码

本文分享自华为云社区《Ascend C保姆级教程:我的第一份Ascend C代码-云社区-华为云》,作者:昇腾CANN 。

Ascend C是昇腾AI异构计算架构CANN针对算子开发场景推出的编程语言,原生支持C和C++标准规范,最大化匹配用户开发习惯;通过多层接口抽象、自动并行计算、孪生调试等关键技术,极大提高算子开发效率,助力AI开发者低成本完成算子开发和模型调优部署。

本文提供Ascend C保姆级教程,从一个简单的实例出发,带你体验Ascend C算子开发的基本流程。

完成实例开发之前,需要先了解一些必备的背景知识。

1. 背景知识多核并行

使用Ascend C开发的算子运行在AI Core上,AI Core是昇腾NPU硬件平台的计算核心,NPU内部有多个AI Core。Ascend C编程过程中会将需要处理的数据拆分同时在多个AI Core上运行,从而获取更高的性能。多个AI Core共享相同的指令代码,每个核上的运行实例唯一的区别是block_idx不同,开发者只需要关注单核上的处理程序,也就是核函数。

流水并行

上文提到,开发者只需要关注单核处理程序(核函数),那么如何实现核函数的具体逻辑呢?Ascend C提供流水线式的编程范式,基于编程范式可以快速搭建算子实现的代码框架,实现流水并行。

流水线并行的概念和工业生产中的流水线是类似的,任务1完成对某片数据的处理后,将其加入到通信队列,任务2空闲时就会从队列中取出数据继续处理;可以类比为生产流水线中的工人只完成某一项固定工序,完成后就交由下一项工序负责人继续处理。

Ascend C编程范式是一种流水线式的编程范式,把算子核内的处理程序,分成多个流水任务:“搬入、计算、搬出”,通过队列(Queue)完成任务间通信和同步,并通过统一的内存管理模块(Pipe)管理任务间通信内存。开发者只需聚焦实现“搬入、计算、搬出”内容。

孪生调试

基于NPU域算子的调用接口编写程序,通过毕昇编译器编译后运行,可以完成算子NPU域的运行验证;基于CPU域算子的调用接口编写程序,通过标准的GCC编译器进行编译后运行,并通过GDB通用调试工具进行单步调试,精准验证程序执行流程是否符合预期。孪生调试的能力,大大提升了算子的调试效率。下文的示例开发,仅介绍核函数CPU侧和NPU侧的运行验证,具体的调试步骤将会在后续的文章中详细介绍。

2. 开发流程

本文将引导你完成以下任务,体验Ascend C算子开发的基本流程。

使用Ascend C完成Add算子核函数开发;使用ICPU_RUN_KF CPU调测宏完成算子核函数CPU侧运行验证;使用<<<>>>内核调用符完成算子核函数NPU侧运行验证。

在正式的开发之前,还需要先完成环境准备和算子分析工作,开发Ascend C算子的基本流程如下图所示:

参考本文进行开发之前请先获取样例代码目录quick-start,该样例代码只保留了部分代码框架,核心代码在下文的指导步骤中体现。您可以在阅读本文时,将指导步骤中的代码拷贝至对应位置,即可快速完成Ascend C算子的开发。

3. 环境准备CANN软件安装

开发算子前,需要先准备好开发环境和运行环境,开发环境和运行环境的介绍和具体的安装步骤可参见昇腾社区文档的CANN软件安装指南。

环境变量配置

安装CANN软件后,使用CANN运行用户编译、运行时,需要以CANN运行用户登录环境,执行source ${install_path}/set_env.sh命令设置环境变量,其中${install_path}为CANN软件的安装目录。

4. 算子分析

主要分析算子的数学表达式、输入、输出以及计算逻辑的实现,明确需要调用的Ascend C接口。

1. 明确算子的数学表达式及计算逻辑。

Add算子的数学表达式为:

z = x + y

计算逻辑是:要完成AI Core上的数据计算,输入数据需要先从外部存储Global Memory搬运进AI Core的内部存储Local Memory,然后使用计算接口完成两个输入参数相加,得到最终结果,再搬出到外部存储Global Memory上。

2. 明确输入和输出。

Add算子有两个输入:x与y,输出为z。本样例中算子的输入支持的数据类型为half(float16),算子输出的数据类型与输入数据类型相同。算子输入支持shape(8,2048),输出shape与输入shape相同。算子输入支持的format为:ND。

3. 确定核函数名称和参数。

您可以自定义核函数名称,本样例中核函数命名为add_custom。根据对算子输入输出的分析,确定核函数有3个参数x,y,z;x,y为输入在Global Memory上的内存地址,z为输出在Global Memory上的内存地址。确定算子实现所需接口。实现涉及外部存储和内部存储间的数据搬运,查看Ascend C API参考中的数据搬移接口,需要使用DataCopy来实现数据搬移。本样例只涉及矢量计算的加法操作,查看Ascend C API参考中的矢量计算接口,初步分析可使用双目指令Add接口实现x+y。计算中使用到的Tensor数据结构(数据操作的基础数据结构),使用AllocTensor、FreeTensor进行申请和释放。并行流水任务之间使用Queue队列完成通信和同步,会使用到EnQue、DeQue等接口。

通过以上分析,得到Ascend C Add算子的设计规格如下:

5 核函数开发

完成环境准备和初步的算子分析后,即可开始Ascend C核函数的开发。开发之前请先获取样例代码目录quick-start(samples: CANN Samples - Gitee),以下核函数开发的样例代码在add_custom.cpp中实现。

本样例中使用多核并行计算,即把数据进行分片,分配到多个核上进行处理。Ascend C核函数是在一个核上的处理函数,所以只处理部分数据。分配方案是:数据整体长度TOTAL_LENGTH为8* 2048,平均分配到8个核上运行,每个核上处理的数据大小BLOCK_LENGTH为2048。下文的核函数,只关注长度为BLOCK_LENGTH的数据应该如何处理。

5.1 核函数的定义

进行核函数的定义,并在核函数中调用算子类的Init和Process函数。请将下文代码添加至add_custom.cpp的“核函数实现”注释处。

extern "C" __global__ __aicore__ void add_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z){KernelAdd op;op.Init(x, y, z);op.Process();}

1. 使用__global__函数类型限定符来标识它是一个核函数,可以被<<<...>>>调用;使用__aicore__函数类型限定符来标识该核函数在设备端AI Core上执行。指针入参变量需要增加变量类型限定符__gm__,表明该指针变量指向Global Memory上某处内存地址为了统一表达,使用GM_ADDR宏来修饰入参,GM_ADDR宏定义如下:

#define GM_ADDR __gm__ uint8_t* __restrict__

2. 算子类的Init函数,完成内存初始化相关工作,Process函数完成算子实现的核心逻辑。

5.2 算子类定义

本样例中定义KernelAdd算子类,其具体成员如下。请将下文代码添加至add_custom.cpp的“算子类实现”注释处。

class KernelAdd {public:__aicore__ inline KernelAdd(){}// 初始化函数,完成内存初始化相关操作__aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR z){}// 核心处理函数,实现算子逻辑,调用私有成员函数CopyIn、Compute、CopyOut完成矢量算子的三级流水操作__aicore__ inline void Process(){}private:// 搬入函数,完成CopyIn阶段的处理,被核心Process函数调用__aicore__ inline void CopyIn(int32_t progress){}// 计算函数,完成Compute阶段的处理,被核心Process函数调用__aicore__ inline void Compute(int32_t progress){}// 搬出函数,完成CopyOut阶段的处理,被核心Process函数调用__aicore__ inline void CopyOut(int32_t progress){}private:TPipe pipe; //Pipe内存管理对象TQue<QuePosition::VECIN, BUFFER_NUM> inQueueX, inQueueY; //输入数据Queue队列管理对象,QuePosition为VECINTQue<QuePosition::VECOUT, BUFFER_NUM> outQueueZ; //输出数据Queue队列管理对象,QuePosition为VECOUTGlobalTensor<half> xGm, yGm, zGm; //管理输入输出Global Memory内存地址的对象,其中xGm, yGm为输入,zGm为输出};

内部函数的调用关系示意图如下:

由此可见除了Init函数完成初始化外,Process中完成了对流水任务:“搬入、计算、搬出”的调用,开发者可以重点关注三个流水任务的实现。

5.3 Init 函数实现

初始化函数Init主要完成以下内容:设置输入输出Global Tensor的Global Memory内存地址,通过Pipe内存管理对象为输入输出Queue分配内存。

上文我们介绍到,本样例将数据切分成8块,平均分配到8个核上运行,每个核上处理的数据大小BLOCK_LENGTH为2048。那么我们是如何实现这种切分的呢?

每个核上处理的数据地址需要在起始地址上增加GetBlockIdx()*BLOCK_LENGTH(每个block处理的数据长度)的偏移来获取。这样也就实现了多核并行计算的数据切分。

以输入x为例,x + BLOCK_LENGTH * GetBlockIdx()即为单核处理程序中x在Global Memory上的内存偏移地址,获取偏移地址后,使用GlobalTensor类的SetGlobalBuffer接口设定该核上Global Memory的起始地址以及长度。具体示意图如下。

上面已经实现了多核数据的切分,那么单核上的处理数据如何进行切分?

对于单核上的处理数据,可以进行数据切块(Tiling),在本示例中,仅作为参考,将数据切分成8块(并不意味着8块就是性能最优)。切分后的每个数据块再次切分成2块,即可开启double buffer,实现流水线之间的并行。

这样单核上的数据(2048个数)被切分成16块,每块TILE_LENGTH(128)个数据。Pipe为inQueueX分配了两块大小为TILE_LENGTH * sizeof(half)个字节的内存块,每个内存块能容纳TILE_LENGTH(128)个half类型数据。数据切分示意图如下。

具体的初始化函数代码如下:

__aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR z){// 多核并行,设定当前核上Global Memory的起始地址以及长度 xGm.SetGlobalBuffer((__gm__ half*)x + BLOCK_LENGTH * GetBlockIdx(), BLOCK_LENGTH);yGm.SetGlobalBuffer((__gm__ half*)y + BLOCK_LENGTH * GetBlockIdx(), BLOCK_LENGTH);zGm.SetGlobalBuffer((__gm__ half*)z + BLOCK_LENGTH * GetBlockIdx(), BLOCK_LENGTH);// 通过pipe为queue分配内存,单位为Bytespipe.InitBuffer(inQueueX, BUFFER_NUM, TILE_LENGTH * sizeof(half));pipe.InitBuffer(inQueueY, BUFFER_NUM, TILE_LENGTH * sizeof(half));pipe.InitBuffer(outQueueZ, BUFFER_NUM, TILE_LENGTH * sizeof(half));}5.4 核心处理函数实现

基于矢量编程范式,将核函数的实现分为3个基本任务:CopyIn,Compute,CopyOut。任务之间通过队列进行通信,交互示意图如下:

Process函数中通过如下方式调用这三个函数。

__aicore__ inline void Process(){// 开启double buffer后循环次数需要乘以2constexpr int32_t loopCount = TILE_NUM * BUFFER_NUM;// 多个任务实现流水并行for (int32_t i = 0; i < loopCount; i++) {CopyIn(i);Compute(i);CopyOut(i);}}CopyIn函数实现。

__aicore__ inline void CopyIn(int32_t progress){// 1、从队列中分配TensorLocalTensor<half> xLocal = inQueueX.AllocTensor<half>();LocalTensor<half> yLocal = inQueueY.AllocTensor<half>();// 2、使用DataCopy接口将GlobalTensor数据拷贝到LocalTensorDataCopy(xLocal, xGm[progress * TILE_LENGTH], TILE_LENGTH);DataCopy(yLocal, yGm[progress * TILE_LENGTH], TILE_LENGTH);// 3、将LocalTensor放入搬入数据的存放位置VecIn的Queue中inQueueX.EnQue(xLocal);inQueueY.EnQue(yLocal);}Compute函数实现。

__aicore__ inline void Compute(int32_t progress){// 1、使用DeQue从VecIn中取出LocalTensorLocalTensor<half> xLocal = inQueueX.DeQue<half>();LocalTensor<half> yLocal = inQueueY.DeQue<half>();LocalTensor<half> zLocal = outQueueZ.AllocTensor<half>();// 2、调用Add指令完成双目矢量计算Add(zLocal, xLocal, yLocal, TILE_LENGTH);// 3、使用EnQue将计算结果LocalTensor放入到搬出数据的存放位置VECOUT的Queue中outQueueZ.EnQue<half>(zLocal);// 4、使用FreeTensor将释放不再使用的LocalTensorinQueueX.FreeTensor(xLocal);inQueueY.FreeTensor(yLocal);}CopyOut函数实现。

__aicore__ inline void CopyOut(int32_t progress){// 1、使用DeQue接口从VecOut的Queue中取出LocalTensorLocalTensor<half> zLocal = outQueueZ.DeQue<half>();// 2、使用DataCopy接口将LocalTensor拷贝到GlobalTensor上DataCopy(zGm[progress * TILE_LENGTH], zLocal, TILE_LENGTH);// 3、使用FreeTensor将不再使用的LocalTensor进行回收outQueueZ.FreeTensor(zLocal);}6 核函数运行验证

异构计算架构中,NPU(kernel侧)与CPU(host侧)是协同工作的,完成了kernel侧核函数开发后,即可编写host侧的核函数调用程序,实现从host侧的APP程序调用算子,执行计算过程。

除了上文核函数实现文件add_custom.cpp外,核函数的调用与验证还需要需要准备以下文件:

调用算子的应用程序:main.cpp。输入数据和真值数据生成脚本文件:add_custom.py。编译cpu侧或npu侧运行的算子的编译工程文件:CMakeLists.txt。编译运行算子的脚本:run.sh。

本文仅介绍调用算子的应用程序的编写,该应用程序在main.cpp中体现,其他内容您可以在quick-start中直接获取。

6.1 host侧应用程序框架编写

内置宏__CCE_KT_TEST__ 是区分运行CPU模式或NPU模式逻辑的标志,在同一个main函数中通过对__CCE_KT_TEST__宏定义的判断来区分CPU和NPU侧的运行程序。

int32_t main(int32_t argc, char* argv[]){size_t inputByteSize = 8 * 2048 * sizeof(uint16_t); // uint16_t represent halfsize_t outputByteSize = 8 * 2048 * sizeof(uint16_t); // uint16_t represent halfuint32_t blockDim = 8;#ifdef __CCE_KT_TEST__// 用于CPU调试的调用程序#else// NPU侧运行算子的调用程序#endifreturn 0;}6.2 CPU运行验证

完成算子核函数CPU侧运行验证的步骤如下:

分配共享内存,并进行数据初始化;调用ICPU_RUN_KF调测宏,完成核函数CPU侧的调用;释放申请的资源。

请将下文代码添加至上面代码框架的“用于CPU调试的调用程序”注释处。

uint8_t* x = (uint8_t*)AscendC::GmAlloc(inputByteSize);uint8_t* y = (uint8_t*)AscendC::GmAlloc(inputByteSize);uint8_t* z = (uint8_t*)AscendC::GmAlloc(outputByteSize);ReadFile("./input/input_x.bin", inputByteSize, x, inputByteSize);ReadFile("./input/input_y.bin", inputByteSize, y, inputByteSize);AscendC::SetKernelMode(KernelMode::AIV_MODE);ICPU_RUN_KF(add_custom, blockDim, x, y, z); // use this macro for cpu debugWriteFile("./output/output_z.bin", z, outputByteSize);AscendC::GmFree((void *)x);AscendC::GmFree((void *)y);AscendC::GmFree((void *)z);6.3 NPU侧运行验证

完成算子核函数NPU侧运行验证的步骤如下:

初始化Device设备;创建Context绑定设备;分配Host内存,并进行数据初始化;分配Device内存,并将数据从Host上拷贝到Device上;用内核调用符<<<>>>调用核函数完成指定的运算;将Device上的运算结果拷贝回Host;释放申请的资源。

请将下文代码添加至上面代码框架的“NPU侧运行算子的调用程序”注释处。

// AscendCL初始化CHECK_ACL(aclInit(nullptr));// 创建Context绑定设备aclrtContext context;int32_t deviceId = 0;CHECK_ACL(aclrtSetDevice(deviceId));CHECK_ACL(aclrtCreateContext(&context, deviceId));aclrtStream stream = nullptr;CHECK_ACL(aclrtCreateStream(&stream));// 分配Host内存,并进行数据初始化uint8_t *xHost, *yHost, *zHost;uint8_t *xDevice, *yDevice, *zDevice;CHECK_ACL(aclrtMallocHost((void**)(&xHost), inputByteSize));CHECK_ACL(aclrtMallocHost((void**)(&yHost), inputByteSize));CHECK_ACL(aclrtMallocHost((void**)(&zHost), outputByteSize));// 分配Device内存,并将数据从Host上拷贝到Device上CHECK_ACL(aclrtMalloc((void**)&xDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));CHECK_ACL(aclrtMalloc((void**)&yDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));CHECK_ACL(aclrtMalloc((void**)&zDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));ReadFile("./input/input_x.bin", inputByteSize, xHost, inputByteSize);ReadFile("./input/input_y.bin", inputByteSize, yHost, inputByteSize);CHECK_ACL(aclrtMemcpy(xDevice, inputByteSize, xHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE));CHECK_ACL(aclrtMemcpy(yDevice, inputByteSize, yHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE));// 用内核调用符<<<>>>调用核函数完成指定的运算add_custom_do(blockDim, nullptr, stream, xDevice, yDevice, zDevice);CHECK_ACL(aclrtSynchronizeStream(stream));// 将Device上的运算结果拷贝回HostCHECK_ACL(aclrtMemcpy(zHost, outputByteSize, zDevice, outputByteSize, ACL_MEMCPY_DEVICE_TO_HOST));WriteFile("./output/output_z.bin", zHost, outputByteSize);// 释放申请的资源CHECK_ACL(aclrtFree(xDevice));CHECK_ACL(aclrtFree(yDevice));CHECK_ACL(aclrtFree(zDevice));CHECK_ACL(aclrtFreeHost(xHost));CHECK_ACL(aclrtFreeHost(yHost));CHECK_ACL(aclrtFreeHost(zHost));CHECK_ACL(aclrtDestroyStream(stream));CHECK_ACL(aclrtDestroyContext(context));CHECK_ACL(aclrtResetDevice(deviceId));CHECK_ACL(aclFinalize());6.4 执行一键式编译运行脚本,编译和运行应用程序

脚本执行方式如下:

bash run.sh <kernel_name> <soc_version> <core_type> <run_mode>

<kernel_name>表示需要运行的算子。<soc_version>表示算子运行的AI处理器型号。<core_type>表示在AiCore上或者VectorCore上运行。<run_mode>表示算子以cpu模式或npu模式运行。

1. CPU模式下执行如下命令(算子运行的AI处理器型号以Ascend 910为例):

bash run.sh add_custom ascend910 AiCore cpu

运行结果如下,当前使用md5sum对比了所有输出bin文件,md5值一致表示实际的输出数据和真值数据相符合。

2. NPU模式下执行如下命令:bash run.sh add_custom ascend910 AiCore npu

运行结果如下,当前使用md5sum对比了所有输出bin文件,md5值一致表示实际的输出数据和真值数据相符合。

至此,你已经完成了Ascend C算子开发的快速入门,更多内容请参考:《Ascend C 官方教程》

号外!

华为将于2023年9月20-22日,在上海世博展览馆和上海世博中心举办第八届华为全联接大会(HUAWEICONNECT 2023)。本次大会以“加速行业智能化”为主题,邀请思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁,从商业、产业、生态等方面探讨如何加速行业智能化。

我们诚邀您莅临现场,分享智能化的机遇和挑战,共商智能化的关键举措,体验智能化技术的创新和应用。您可以:

在100+场主题演讲、峰会、论坛中,碰撞加速行业智能化的观点参观17000平米展区,近距离感受智能化技术在行业中的创新和应用与技术专家面对面交流,了解最新的解决方案、开发工具并动手实践与客户和伙伴共寻商机

感谢您一如既往的支持和信赖,我们热忱期待与您在上海见面。

大会官网:https://www.huawei/cn/events/huaweiconnect

欢迎关注“华为云开发者联盟”公众号,获取大会议程、精彩活动和前沿干货。

关注#华为云开发者联盟# 点击下方,第一时间了解华为云新鲜技术~

华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云

《我的世界》教你建造刷钻机 从此腰缠万贯走上人生巅峰

冒险家们在游戏中很难获取大量钻石,就算你找到了钻石你可能还会掉入岩浆…所以我们今天就教大家怎么在游戏里做一个超级刷钻机~只需要一个钻石就可以一劳永逸

超级刷钻机

要做出超级刷钻机其实十分简单~只需要一个命令方块即可,在聊天栏输入/give @s command_block得到一个命令方块

挖一格地面将其放在里面点开并输入clone~~1~~~1~~~3~方块类型选择循环,条件设置成无条件,红石选择保持开启

接着我们在这个命令方块上面放置一个箱子,我们会发现在箱子上方自动复制了一个箱子

我们打开下方的箱子放入两个钻石

超级刷钻机就完成啦~现在我们将上方的箱子摧毁,可以发现会有钻石掉落出来而且被摧毁后会立刻复制一个与下方相同的箱子(包括箱内物品)

不到一会儿背包就已经塞不下去啦~

这种腰缠万贯的感觉——爽!

冻住它们!

当你靠近一个生物就可以将其冰冻住,这是什么感觉呢?我们拿到一个命令方块

打开后输入execute @p ~ ~ ~execute @e[type=!player,r=6]~ ~ ~ fill ~-1 ~-1 ~-1 ~1 ~2 ~1 ice 0 hollow方块类型选择循环,条件设置成无条件,红石选择保持开启,只要我们靠近生物马上可以将它们冻住

苦力怕看见我们都要绕着道走实在是太帅啦~

在使用这个指令时冒险家们需要注意,当我们接近掉落物时掉落物也会被冰冻住,所以如果冒险家们想要扔掉一些物品记得先将指令关闭哦~否则就会像我一样把自己困在冰里…

看完我们分享的指令小技巧冒险家们赶快去游戏内试试啦如果大家还知道别的有趣的指令欢迎在评论区留言哦~

更多资讯
游戏推荐
更多+