精华内容
下载资源
问答
  • 而使用最频繁的以 if 为首的可能会产生分支的流程控制指令,所以本文也主要围绕 if 和分支进行讨论。1.对 if 的传统理解在 Shader 中,尽量避免使用 if 已成为绝大多数开发者的共识,究其原因认为 if 会打断 GPU...

    9025f6727dbb517858ea59b5eb81fbe1.png

    Shader 中提供了一些流程控制指令,如 if、for、while、switch、discard等。而使用最频繁的是以 if 为首的可能会产生分支的流程控制指令,所以本文也主要围绕 if 和分支进行讨论。

    1.对 if 的传统理解

    在 Shader 中,尽量避免使用 if 已成为绝大多数开发者的共识,究其原因是认为 if 会打断 GPU 的 warp内部(或者 wavefront,下文统称 warp)的并行化。

    一种“优化”思路是规避 if 关键字,用内置指令替代,如 step 等。如:

    if(cx > cy)
    {
        x = a;
    }
    else
    {
        x = b;
    }

    用内置指令优化一下:

    lerp(a, b, step(cx, cy));

    或者采取更“花哨”的内置函数组合封装一些通用判断方法,如这篇帖子:Avoiding Shader Conditionals。以文中的 when_gt 示例:

    float when_gt(float x, float y) {
      return max(sign(x - y), 0.0);
    }
    
    col += 0.5 * when_gt(IN.uv.x, IN.uv.y));

    对应的汇编指令如下:

       0: add r0.x, -v0.y, v0.x
       1: lt r0.y, l(0.000000), r0.x
       2: lt r0.x, r0.x, l(0.000000)
       3: iadd r0.x, -r0.y, r0.x
       4: itof r0.x, r0.x
       5: max r0.x, r0.x, l(0.000000)
       6: lt r0.y, v0.y, v0.x
       7: movc r0.y, r0.y, l(0.600000), l(0.100000)
       8: mad o0.xyzw, r0.xxxx, l(0.500000, 0.500000, 0.500000, 0.500000), r0.yyyy
       9: ret 

    对照 if 表达式:

    if (x > y)
    {
         col += 0.5; 
    }

    生成的汇编指令:

       0: lt r0.x, v0.y, v0.x
       1: movc o0.xyzw, r0.xxxx, l(0.600000,0.600000,0.600000,0.600000), l(0.100000,0.100000,0.100000,0.100000)
       2: ret 

    "if 表达式" 版本只有1条比较指令和 movc 指令,而 "when_gt" 生成的指令数更多、成了负优化,执行效率上来说 if 表达式完胜。(当然每条指令在硬件上执行的 cycle 不同,如果对照的指令差别很大,则不能纯粹通过比较指令数来衡量最终的GPU执行效率。)

    2. if ≠ 分支

    从上面 if 生成的指令来看,if 没有生成分支指令。事实上,对于简单逻辑而言,编译器大多生成的是一条现代GPU会硬件支持的 "select"(或称为 "conditional move")指令,在D3D、PowerVR 等指令集中对应的就是一条 movc 指令。

    所以,if 有可能生成分支,也有可能生成 "select"。if 不等于分支,我们真正要规避的是分支,而不是 if。if 最终是否会生成和执行分支指令,取决于具体厂商的“编译器 + driver + GPU”。像苹果就明确让开发者使用 ternary 操作符进行 "select",并且在A8及之后的 GPU 都对 ternary 提供硬件支持:

    448b0780a7b9798c37f11fedeb5cb9f3.png
    Advanced Metal Shader Optimization - WWDC 2016 - Videos - Apple Developerdeveloper.apple.com
    e64e201c1265bdbd6ad731caf51d3803.png

    对于 "select" ,我们可以通过iq大佬给出的一段测试用例("Select" Test),比较下不同的写法和对应的性能表现:

    // conditional move
    #if METHOD==0
    col = (p.x<h && p.y<h+h) ? col+tmp : col;
    #endif
            
    // "smart" way
    #if METHOD==1
    col += tmp*step(p.x,h)*step(p.y,h+h);
    #endif
            
    // even "smarter" way
    #if METHOD==2
    col += tmp*float(p.x<h && p.y<h+h);
    #endif
    
    
    // conditional branching
    #if METHOD==3
    if( p.x<h && p.y<h+h ) col += tmp;
    #endif

    另外像D3D或成熟的商用引擎如 UE4、U3D 等还提供了 flatten、branch 等流程控制关键字,根据对应的关键字编译成对应的流程控制指令或者翻译成对应的目标平台代码。

    flatten 把分支所有侧的逻辑都执行一遍,根据判断条件选择其中一个结果;而 branch 则是一次执行分支中的一侧逻辑。(在具体使用时,编译器会判定这两个关键字是否会产生副作用,如果有就会让该关键字失效,可参考微软或其它相关文档。)

    在大多数没有标明关键字的情况下,编译器默认生成的是 flatten 形式的指令。所以,在METHOD 3的基础上补充一条:

    #if METHOD == 4
    [branch]
    if (uv.x < uv.z && uv.y < uv.z + uv.z) col += tmp;
    #endif

    在PC上(分辨率=1280 x 800,Graphics API = Direct3D 11.0,GPU = GTX 970,LoopNum = 1000),Method 0~4 的测试结果从上至下依次展示在下图中:

    066dcda3618021b2244ba69ec9ed4b74.png
    Test Selection

    而在Android设备上(分辨率=1440 x 720,Graphics API = OpenGL ES 3.2,GPU = Mali-G72,LoopNum = 150),Method 0~4 的测试结果从上至下依次展示在下图中:

    f8327a2563f43e9951d775cde8402646.png

    PC上最终的测试帧率:0 = 3 >= 2 > 1 > 4。Method 0 和 3生成的指令一样,Method 2在指令数上一致,只是指令略有不同,所以执行效率上有些差异。而 Method 1在本地测试平台生成的指令数较0、3、2多所以性能较低。Method 4 则是生成了分支所以性能最差。但在本地安卓设备上Method 4 和 3 测试结果几乎一致,说明并没有生成分支。(测试结果仅供参考,在不同设备上可能表现略有不同,以具体设备的 profile 为准。)

    3.分支

    分支无法避免,如果最终生成了分支指令,能尝试做些什么?首先从渲染流水线的角度来看,我们使用最频繁的还是 vertex shader 和 pixel shader,所以我们仅考虑这两个 stage。

    而分支大体可以分为:

    • static branch:如判断变量为 uniform 类型;
    • dynamic branch & invariant(coherent):如判断变量为 varying类型,且在 warp中 有相关性甚至一致;
    • dynamic branch & variant(incoherent) :如判断变量为 varying类型,但在 warp中 无任何相关性。

    静态分支性能上几乎无损,而“ps + varaiant”分支则是需要尽量避免的。如有可能,优化方向尽量从 "ps + variant" -->“ps + invariant" 或 "vs + variant" --> "vs + invariant" ……

    这里针对 pixel shader 中 invariant 和 variant 简单做了个实验(复用2中的测试用例代码逻辑,测试设备相同)。另外提供一组 block size 不同的噪声图,block size 用来模拟动态分支下不同程度的相关性。将噪声值作为判断条件,渲染一个三角形到全屏:

    cf86bc7be391d3e4b5eb2c0b905684cb.png

    ff4dac83b566d43d8c57bad13caa90d8.png
    PC测试结果(LoopNum=1000,block size 从 1x1 到 32x32)

    3909cae482945dfc5cbe2ebfbd2c6fcf.png
    Android测试结果(LoopNum=100,block size 从 1x1 到 8x8)

    可以看到随着 block size 越来越大即分支相关性越高,帧率也越来越高。(测试结果仅供参考,以具体设备 profile 为准。)

    需要附加说明的是:

    • 这一论证需要有个前置条件,GPU的像素绘制顺序 应该是按一定规律的方向顺序绘制;
    • 在移动平台上,当 block size 大到一定程度的时候(本地android设备是 8x8),帧率不变。猜测是移动平台上的 Tile-Based 特性导致,而且 tile 大小很有可能就是 8x8 的块,而 warp 处理的 thread 数量应该小于等于 64。

    PS.现代GPU编译器

    对于1中给出的 lerp + step“优化”方案,直觉上应该也会生成比 if表达式更多的指令,但聪明的编译器给出的结果是:

       0: ge r0.x, v0.y, v0.x
       1: movc o0.xyzw, r0.xxxx, l(1.100000,1.100000,1.100000,1.100000), l(0.600000,0.600000,0.600000,0.600000)

    跟 if 几乎一致(比较指令不同而已)。现代GPU编译器已经能够很好的优化代码,不用像 GPU Gem介绍的Shader优化 需要手动整理成硬件友好的 mad、rcp 等形式,而且大量的 swizzle 技巧也是编译器的常规操作。手动调整一方面会影响代码可读性,另一方面带来的收益性价比不高。当然在 ALU 成为绝对瓶颈的情况下,可以进行尝试。

    展开全文
  • 一、什么是Python控制流...在Python中,有三种 控制流类型,第一种是顺序结构,就是指按顺序执行的结构,第二种是分支结构,第 三种是循环结构。1.顺序结构:顺序结构2.分支结构ifif结构基本语法: if 判断语句1: ...

    ab22acf44dea750a89bdcecb0119d8df.png

    一、什么是Python控制流?

    在Python中通常的情况下程序的执行是从上往下执行的,而某些时候我们为了改变程序的执行顺序,故而使用控制流语句控制程序怎么执行。

    二、Python控制流有哪些类型?

    在Python中,有三种 控制流类型,

    第一种是顺序结构,就是指按顺序执行的结构,

    第二种是分支结构,

    第 三种是循环结构。

    1.顺序结构:

    e628a932a857f8ff6ee10a076ef49c47.png
    顺序结构

    2.分支结构if

    if结构基本语法
    
    if 判断语句1:
        执行结果1
    elif 判断语句2
        执行结果2
    else 判断语句3
        执行结果3

    (1)只有一种选择if

    58644e29bd47f68e7d192cada86da702.png

    (2)有两种选择if .. else ...

    9188deede29ca15aa43e266e40918f98.png

    (3)有两种以上的选择elif来添加条件if ...elif....else...

    d3cf617fbc2d521be68d5aabd6638426.png

    if语句要点:“各分支尽量不重复,并且尽量包含全部可能性”

    比如我们要按成绩高低分为优良差,比如这样划分的条件是比较合理的0<=成绩<80为差,80<=成绩<90为良,90<=成绩<=100为优。

    而这样划分的条件是不合理的:

    1)0<成绩<=80为差,80<=成绩<90为良,90<=成绩<100。80重合了;

    2)0<成绩<80为差,80<成绩<90为良,90<=成绩<100。比如如果一个人成绩是0分、100分、80分、90分这种临界条件的时候就没办法判断执行哪部分语句了。

    3.循环结构while...(else...)

    while 条件为真:
        循环执行“该部分语句
        执行该部分语句
        执行该部分语句”
    else:
        如果条件为假,执行该部分语句

    其中else语句可以省略。

    ae57e483a42b187ab1e40f944396a968.png

    1f09501d8b08b3b6cf60676564e401da.png

    63001c052a5ea3faab0ad3a9d320fc86.png

    4.循环结构for

    for语句格式:
    for i in 集合:
        执行该部分
    else:
        执行该部分

    0b8005b1e995eb10606a47b85910863f.png

    (2)for与range函数的使用

    python range() 函数可创建一个整数列表,一般用在 for 循环中。

    函数语法

    range(start, stop[, step])

    参数说明:

    • start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
    • stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
    • step:步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)

    06a4e89f95fbde7cf55de0fad6304322.png

    (3)for经常与Python中的迭代器一起使用

    什么是“迭代器”?

    迭代是Python最强大的功能之一,是访问集合元素的一种方式。
    迭代器是一个可以记住遍历的位置的对象。
    迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
    迭代器有两个基本的方法:iter() 和 next()。
    字符串,列表或元组对象都可用于创建迭代器:
    
    列表迭代器 
    list=[1,2,3,4]
    it = iter(list)    # 创建迭代器对象
    for x in it:
        print (x, end=" ")

    adf2f75a0db09df2064917f377bf5f47.png

    b6a58ff67959bfad10e8b0f79de9b93f.png

    (4)带嵌套的for语句

    d124662e005206fbac70de8cacc06187.png

    三.Break语句

    Break语句的功能正如其名字一样,是用来打破(Break)程序的执行的。Break语句常用于循环结构中,在循环结构中出现Break语句的时候,能将该循环强制停止,然后退出该循环。

    (1)break语句用在while循环中

    dff1849ee945e7e3e1073c04603487c4.png

    (2)break语句在for循环中

    161e26523cc5d6a9865be8248d443190.png

    (3)break语句在双层循环语句中

    064d25bee50cf19efe52204f3806e246.png

    23720b8f06f08eb3f421150e47962439.png

    四、Continue语句

    Continue语句的功能是强制停止循环中的这一次执行,直接跳到下一次执行。

    (1)continue在while循环中

    a193d9f3f0f85b7acc4ea9bc62bd5e39.png

    (1)continue在for循环中

    49009d53b374ad0255310341e03680f8.png

    f209c2de5fe5849247c0ceb0da2469cf.png

    (3)continue语句在双层循环语句中

    3a8069aad1223e81c59b3f749037bb82.png

    (4)continue和break区别:

    break是停止循环,continue是停止这次执行直接进行下一次循环

    8d7903d1807c93d1a30c103a2d314d23.png

    cc464a48070a4c89cd49a499d1fd7d3f.png

    五、列表生成式

    列表生成式是快速生成一个列表的一些公式

    1.在列表中存放0~100的数:

    普通的列表生成:

    numbers=[] 
    for x in range(0,101): 
     numbers.append(x) 
    print(numbers) 

    用列表生成式生成列表:[要放入列表的数据 简单的表达式1 表达式2]

    #x for x in range(0,101) for循环遍历出来的值,放入列表中 
    
    numbers=[x for x in range(0,101)] 
    print(numbers)  

    6302ac48ecb9072631f3e75f47332abc.png

    2.列表中存放0~100的偶数:

    普通方法生成列表:

    numbers=[] 
    for x in range(0,101): 
     if x%2==0: 
      numbers.append(x) 
    print(numbers)

    aa0790e9e97c7b49a67eb31885fd7dd9.png

    用列表生成式生成列表:

    #for循环遍历0~101的数字,如果数字对2取余==0,表示是偶数,x放在列表中 
    numbers=[x for x in range(0,101)if x%2==0] 
    print(numbers)  

    88ae77f826d1b6129b73a1c96de2055f.png

    3.找出列表list1=['asd','adf','dafg','acbo']带有a的字符

    普通写法:

    rs_list=[] 
    for s in list1: 
     if 'a' in s: 
     rs_list.append(s) 
    print(rs_list)

    ddf3f285a72f2b9167c37ad8574198b3.png

    列表生成式:

    list1=['asd','adf','dafg','acbo']
    list2=[x for x in list1 if 'a' in x] 
    print list2

    68e74879121d3a1b7682f24bae5d2ebb.png
    展开全文
  • 在 C语言 的 switch(开关语句)中,break 语句还可用来在执行完一个 case(分支)后立即跳出当前 switch 结构。在某些程序调试过程中则使用break设置断点。下面我们就介绍一下break在PHP中的作用。break 结束当前 for,...

    7a55832f1f8b772dbfb49d9e91144cd9.png

    break 在一些计算机编程语言中是保留字,其作用大多情况下是终止所在层的循环。在 C语言 的 switch(开关语句)中,break 语句还可用来在执行完一个 case(分支)后立即跳出当前 switch 结构。在某些程序调试过程中则使用break设置断点。下面我们就介绍一下break在PHP中的作用。

    break 结束当前 for,foreach,while,do-while 或者 switch 结构的执行。

    break 可以接受一个可选的数字参数来决定跳出几重循环。<?php

    $arr = array('one', 'two', 'three', 'four', 'stop', 'five');

    while (list (, $val) = each($arr)) {

    if ($val == 'stop') {

    break; /* You could also write 'break 1;' here. */

    }

    echo "$val
    /n";

    }

    /* Using the optional argument. */

    $i = 0;

    while (++$i) {

    switch ($i) {

    case 5:

    echo "At 5
    /n";

    break 1; /* Exit only the switch. */

    case 10:

    echo "At 10; quitting
    /n";

    break 2; /* Exit the switch and the while. */

    default:

    break;

    }

    }

    ?>

    展开全文
  • elif-else循环结构:for循环(range)、while循环、continue、break、if-elif-else(链式条件)当我们面对的可能性不只两种,需要更多的分支,就用到了链式条件,elif(else if的缩写),elif分支是无限制的,如果有else...

    day 3 分支结构和循环

    if-elif-else

    循环结构:for循环(range)、while循环、continue、break、

    if-elif-else(链式条件)

    当我们面对的可能性不只两种,需要更多的分支,就用到了链式条件,elif(else if的缩写),elif分支是无限制的,如果有else语句,必须放在条件链的末尾,else不是必须的。

    当后面的条件是在前面条件不成立的情况下进行。如果有一个以上的条件为真,只有先出现的为真的条件所对应的分支语句会运行。

    if嵌套与三目运算符

    if嵌套(如果一个条件判断嵌套在另一个条件判断语句中)

    语法:

    if 条件语句:

    if 条件语句:

    代码段

    else:

    代码段

    else:

    代码段

    三目运算符

    表达式2 if 表达式1 else 表达式3

    1)C的三目运算符

    表达式1 ? 表达式2 :表达式3 - 判断表达式1是否为真,如果是,运算结果就是表达式2,否则就是表达式3

    ?: a>b ? a:b

    2)python的三目运算符

    表达式2 if 表达式1 else 表达式3 - 判断表达式1是否为真,如果是,运算结果就是表达式2,否则就是表达式3

    循环条件

    1.for循环

    语法:

    for 变量 in 序列:

    循环体

    说明:

    1)for - 关键字:固定写法

    2)变量 - 和定义变量的时候变量名的要求是一样的;(如果这个变量名在这个环境中不使用,变量名可以用_代替)

    3)in - 关键字;固定写法

    4)序列 - python中容器型数据类型,例如:字符串、列表、元组、集合、字典、迭代器、生成器、range等

    5): - 固定写法

    6)循环体 - 和for保持一个缩进的一条或多条语句;需要重复执行的代码

    执行过程:让变量去序列中取值,一个一个的取,取完为止,每取一个就执行一次循环体。

    for循环的执行次数,看序列中元素的个数

    2.range函数 - 产生指定范围的数字序列

    range(N) 产生一个[0,N)的数字序列(N是正整数);

    range(N,M)产生一个[N,M-1)的数字序列

    range(N,M,sep)sep为步长

    while循环

    语法

    while 条件语句:

    循环体

    1)while -关键字:固定写法

    2)条件语句:任何有结果的表达 式

    2.1):-固定写法

    3)循环体 -while一个缩进的一个或多个语句,重复执行的语句

    # 练习:写程序让客户不断输入内容,直到输入的内容是0为止。

    msg ='start'

    while msg!='0':

    msg=input('请输入:')

    print('结束')

    for循环以及while循环的选择

    循环次数确定选择for,循环次数不确定选择用while

    原文链接:https://blog.csdn.net/qq_43727147/article/details/110983905

    展开全文
  • 从程序流程的角度看,程序可以分为三种基本结构:顺序结构、分支结构、循环结构。这三种基本结构可以组成各种复杂程序。C语言提供了多种语句来实现这些程序结构。现在,就让我们来看看这些基本语句及其应用。C程序的...
  • then分支2...else分支nfi说明:测试条件1为真,则执行分支1退出;测试条件1为假,则判断测试2是否为真,根据返回值来决定是否执行分支2 ;后续分支同理。示例:传递一个用户给脚本:如果此用户的id为0,则显示说这...
  • python流程控制之while

    2020-06-11 17:23:11
    while是没有次数限制,不知道什么时候结束 只要满足条件就会无限打印 'hello world' ''' 例子2 ''' 当把 while 循环下面的子分支执行完毕以后,程序会返回while条件判断语句 是一个加强版的if ''' while 4>3: ...
  • shell脚本中,这些判断语句一般都和if、else、elif、for和while等语句一起使用。 在脚本编写中,条件判断语句常常用于多种情况的判断,符合哪一种情况就执行哪一种的命令。二、shell条件判断语句:if1、流程控制:...
  • 构造循环外部的代码以实现这一点通常一个好主意,无论它的分支来允许更好的循环结构,还是部分剥离第一次迭代,这样您就可以在一个方便的点进入循环,并可能保存一个mov指令或它里面的任何东西(不确定是否有名称...
  • if(root_num>1)//连通分支大于1,森林,不是树 flag=false; if(flag) printf("Case %d is a tree.\n",i++); else printf("Case %d is not a tree.\n",i++); init();//为下一个case处理进行初始化 ...
  • 函数和局部作用域

    2017-10-27 10:52:11
    在前一篇:循环语句中已经接触到代码的重用了,为了缩减代码长度,开始使用循环语句for和while,回忆一下,格式 for i in range(x, y): 和 while i  啰嗦了这么多,下面开始讲述python中的函数使用规则。  ...
  • 模拟器内核使用的https://android.googlesource.com/kernel/goldfish.git android-goldfish-3.18-dev这个分支,模拟器系统自己编译的android 6.0.1.然后启动的时候debug信息如下: 内核和系统都x86架构。 ...
  • 四则运算2—单元测试

    2015-03-15 20:56:00
    思路:编写的程序只有一个主函数,并且每个要求的实现方式都一层层嵌套的,也都使用同样的方法,先用while对不符合的输入进行提示,再用if...else...分支结构选择。所以则以(是否有乘除法—是否是否有负数—...
  • 用switch分支写的,但是我的实行不了,以下我写的,选4 无法退出。 如果将上面的while(num<=3),则选4的时候可以退出循环,但是当选的不是1234的时候提示重新输入就直接退出循环了。比如我输入不是1,2,3...
  • 我将while循环里的switch分支语句剪切暂时删掉,内部错误消失,成功编译。但是这样就不能实现我的功能了,我自然不安心。 我就又将同样的分支语句(剪切板里的)粘贴在while循序里。...
  • 1.8.1 if...else分支结构 1.8.2 if...else嵌套 1.8.3 switch语句 1.8.4 编程实例 1.9 循环结构 1.9.1 while循环 1.9.2 do…while循环 1.9.3 for循环 1.9.4 编程实例 1.10 跳转结构 1.10.1 break 1.10.2 continue ...
  • 11. 顺序结构、分支结构(或称选择结构)、____________结构化程序设计的三种基本流程控制结构。 12. 以下方法 m 的功能求两参数之积的整数部分。 int m ( float x, float y ) { __________________; } 13. Java ...
  • 疯狂JAVA讲义

    2014-10-17 13:35:01
    4.3.2 do while循环语句 79 4.3.3 for循环 80 4.3.4 嵌套循环 83 4.4 控制循环结构 84 4.4.1 使用break结束循环 84 4.4.2 使用continue结束本次循环 86 4.4.3 使用return结束方法 87 4.5 数组类型 87 4.5.1 ...
  • 20.3.2 编译器应该预测分支吗 342 推荐阅读 343 习题 343 第21章 存储层次 346 21.1 cache的组织结构 346 21.2 cache块对齐 349 21.3 预取 350 21.4 循环交换 354 21.5 分块 355 21.6 垃圾收集和存储层次 357 推荐...
  •  While the practice of mathematics previously developed in other civilizations, the special interests for its theoretical and foundational aspects really started with Ancient Greeks. Early Greek ...
  • 假如不知道机器内的程序中的各语句实际上什么,分别输入什么样的detax来测试出while语句的循环条件写错了。 (4)、把原程序中while语句之前的y=1/x语句去掉,观察程序的运行将会发生什么样的变化。 假如不知道...
  • MFC的程序框架剖析

    2015-03-05 09:53:19
    (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数...
  • 第6章 分支语句和逻辑运算符 6.1 if语句 6.1.1 if else语句 6.1.2 格式化if else语句 6.1.3 if else if else结构 6.2 逻辑表达式 6.2.1 逻辑OR运算符:|| 6.2.2 逻辑AND运算符:&& 6.2.3 用&&来设置取值范围...

空空如也

空空如也

1 2
收藏数 25
精华内容 10
关键字:

while是分支吗