Skip to content

计算图优化:自动inplace化 #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
miaobyte opened this issue Apr 26, 2025 · 1 comment
Open

计算图优化:自动inplace化 #32

miaobyte opened this issue Apr 26, 2025 · 1 comment

Comments

@miaobyte
Copy link
Contributor

如下,这是sigmoid的ir序列,可以忽略调print的部分

需求是,实现一个ir序列的编译器,能够减少中间变量,实现自动inplace化

等待大家挑战这个编译需求

~load(var /home/lipeng/model/deepxmodel/functional/sigmoided)->()
~print(tensor sigmoided, var %.4f)->()
shape:[3, 4, 5]
[0]=[
[-3.0000 -2.9000 -2.8000 -2.7000 -2.6000],
[-2.5000 -2.4000 -2.3000 -2.2000 -2.1000],
[-2.0000 -1.9000 -1.8000 -1.7000 -1.6000],
[-1.5000 -1.4000 -1.3000 -1.2000 -1.1000]
]
[1]=[
[-1.0000 -0.9000 -0.8000 -0.7000 -0.6000],
[-0.5000 -0.4000 -0.3000 -0.2000 -0.1000],
[0.0000 0.1000 0.2000 0.3000 0.4000],
[0.5000 0.6000 0.7000 0.8000 0.9000]
]
[2]=[
[1.0000 1.1000 1.2000 1.3000 1.4000],
[1.5000 1.6000 1.7000 1.8000 1.9000],
[2.0000 2.1000 2.2000 2.3000 2.4000],
[2.5000 2.6000 2.7000 2.8000 2.9000]
]
//从这里开始,是
~newtensor(vector [3 4 5])->(tensor 0)
~mulscalar(tensor sigmoided, var -1)->(tensor 0)
~newtensor(vector [3 4 5])->(tensor 1)
~exp(tensor 0)->(tensor 1)
~newtensor(vector [3 4 5])->(tensor 2)
~addscalar(tensor 1, var 1)->(tensor 2)
~newtensor(vector [3 4 5])->(tensor 3)
~rdivscalar(var 1, tensor 2)->(tensor 3)
//到这里结束
~print(tensor 3, var %.4f)->()
shape:[3, 4, 5]
[0]=[
[0.0474 0.0522 0.0573 0.0630 0.0691],
[0.0759 0.0832 0.0911 0.0998 0.1091],
[0.1192 0.1301 0.1419 0.1545 0.1680],
[0.1824 0.1978 0.2142 0.2315 0.2497]
]
[1]=[
[0.2689 0.2891 0.3100 0.3318 0.3543],
[0.3775 0.4013 0.4256 0.4502 0.4750],
[0.5000 0.5250 0.5498 0.5744 0.5987],
[0.6225 0.6457 0.6682 0.6900 0.7109]
]
[2]=[
[0.7311 0.7503 0.7685 0.7858 0.8022],
[0.8176 0.8320 0.8455 0.8581 0.8699],
[0.8808 0.8909 0.9002 0.9089 0.9168],
[0.9241 0.9309 0.9370 0.9427 0.9478]
]

@miaobyte
Copy link
Contributor Author

参考

————————————————————————————————————————————————————————

以下是实现自动inplace化的完整代码,基于活跃变量分析复用输入变量(args)作为输出变量(returns),避免中间变量:

代码实现

python

def compute_live_after(ir_list):
"""反向计算每个操作后的活跃变量(变量值在后续会被使用)"""
n = len(ir_list)
live_after = [set() for _ in range(n + 1)] # live_after[i]表示操作i执行后的活跃变量集合
for i in reversed(range(n)):
args, returns = ir_list[i][1], ir_list[i][2]
def_vars = set(returns) # 当前操作定义(修改)的变量
use_vars = set(args) # 当前操作使用的输入变量(原始值)

    live_next = live_after[i + 1]
    # 活跃变量:后续活跃的变量(未被当前操作修改) + 当前操作使用的输入变量(原始值若未被修改)
    live_after[i] = (live_next - def_vars) | use_vars
return live_after

def inplace_optimize(ir_list):
"""将IR数组转换为inplace化版本,复用输入变量减少中间变量"""
if not ir_list:
return []

live_after = compute_live_after(ir_list)
optimized_ir = []

for i, (op, args, returns) in enumerate(ir_list):
    # 收集可复用的输入变量:args中在当前操作后不再活跃的变量(原始值无需保留)
    candidates = [var for var in args if var not in live_after[i]]
    
    # 按returns顺序分配候选变量,优先复用输入变量
    new_returns = []
    used_candidates = set()
    for r_var in returns:
        # 寻找第一个未使用的候选变量
        for c_var in candidates:
            if c_var not in used_candidates:
                new_returns.append(c_var)
                used_candidates.add(c_var)
                break
        else:
            # 无可用候选(理论上不应发生,因returns可复用活跃变量的新值)
            # 若必须创建新变量,可在此处生成(示例中假设returns≤args且candidates足够)
            # 实际场景需处理新变量生成,此处简化为复用任意args变量(包括活跃变量)
            # 修正:允许复用活跃变量(新值会被后续使用),故候选应为所有args变量
            # 重新生成候选(包含所有args变量,优先不活跃的)
            candidates_all = args.copy()
            for c_var in candidates_all:
                if c_var not in used_candidates:
                    new_returns.append(c_var)
                    used_candidates.add(c_var)
                    break
    
    optimized_ir.append((op, args, new_returns))

return optimized_ir

 

代码说明

  1. 输入输出格式
  • 输入 ir_list :每个元素为三元组 (op, args, returns) ,例如:
     ("add", [1, 2], [3])  表示  add(1, 2) → 3 (原始IR,使用中间变量3)。

  • 输出:inplace化后的IR,例如:
     ("add", [1, 2], [1])  表示  add(1, 2) → 1 (复用输入变量1,无中间变量)。

  1. 核心逻辑
  • 活跃变量分析( compute_live_after ):
    从后往前遍历IR,计算每个操作执行后哪些变量的原始值仍会被后续操作使用(即未被当前操作修改且后续需要)。

  •  def_vars :当前操作会修改的变量( returns 中的变量)。

  •  live_after[i] :操作 i 执行后,原始值仍活跃的变量(后续需要其未被修改前的值)。

  • 变量复用( inplace_optimize ):
    对每个操作,优先选择 args 中原始值不再活跃的变量( candidates )作为 returns 的目标。

  • 若所有 args 变量的原始值均活跃(如首次操作),则允许复用任意 args 变量(因后续操作会使用其新值,而非原始值)。

  • 按 returns 顺序匹配候选变量,确保一一对应,避免重复复用。

  1. 关键优化点
  • 无中间变量创建:通过直接复用 args 中的变量(即使其活跃,也会被修改为新值供后续使用),消除 returns 中的临时变量。

  • 安全性保证:仅当 returns 包含 args 变量时,才会修改其值,且后续操作通过同一变量名引用新值,符合IR规则。

示例验证

输入:计算  (a + b) * (c + d)  的原始IR

python

original_ir = [
("add", ["a", "b"], ["tmp1"]), # 步骤1:a+b→tmp1(中间变量)
("add", ["c", "d"], ["tmp2"]), # 步骤2:c+d→tmp2(中间变量)
("mul", ["tmp1", "tmp2"], ["res"]) # 步骤3:tmp1*tmp2→res(中间变量)
]
 

输出:inplace化后的IR

python

optimized_ir = inplace_optimize(original_ir)
print(optimized_ir)
 

输出结果

python

[
("add", ["a", "b"], ["a"]), # 复用a存储a+b(a的值被修改,b不变)
("add", ["c", "d"], ["c"]), # 复用c存储c+d(c的值被修改,d不变)
("mul", ["a", "c"], ["a"]) # 复用a存储(a+b)*(c+d)(最终结果存于a)
]
 

验证说明

  • 步骤1: a 的原始值在后续步骤中不再需要(步骤3使用的是 a 的新值 a+b ),故复用 a 。

  • 步骤2: c 的原始值在后续步骤中不再需要,故复用 c 。

  • 步骤3: a 和 c 的当前值(已修改为 a+b 和 c+d )是后续需要的,复用 a 存储最终结果(无后续操作,安全)。

适用场景

  • 两参一返回操作(如 add 、 sub 、 mul 、 div ):直接复用第一个输入变量作为返回目标,消除中间变量。

  • 多返回值操作:按顺序复用输入变量(如 args[0] 、 args[1] 对应 returns[0] 、 returns[1] )。

  • 链式运算:每一步结果直接存储在输入变量中,形成流水线式inplace计算(如 a = ((a + b) - c) * d )。

该代码通过活跃变量分析和变量复用策略,在不引入显式inplace操作的前提下,高效减少中间变量,符合编译原理中数据流优化的核心思想。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant