From 36985b42e99fc1250eccc7a7996678c636978aa6 Mon Sep 17 00:00:00 2001 From: wangfiox Date: Fri, 19 Apr 2024 21:53:22 +0800 Subject: [PATCH] qbqp --- .gitignore | 1 + Cargo.lock | 7 +++ Cargo.toml | 8 ++++ README.md | 82 ++++++++++++++++++++++++++++++++++ src/main.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b685dc8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "pbqp" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8bfff30 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pbqp" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..361ad1a --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# QBQP 算法 + +寄存器分配算法。 + +## 约束方程的含义 + +主要就是约束方程了: + +$$ +min\{ \sum_{i \le i \lt j \le n} X_i^T C_{ij} X_j + \sum_{i=1}^n s_i X_i \} +$$ + +- $X_i = (x_{i, sp}, x_{i, r_1}, x_{i, r_2}, ..., x_{i, r_K})^T$ + - 这里 $x_{i, sp}$ 表示将 $t_i$ spill -> stack 的代价。 + - $X_i = (x, xxx, ..., x)^T$ 只有一个 分量=1,其他都是 0 + - K 表示 $t_i$ 有 K 个可选的寄存器(不一定是所有的 physical reg,这是显然的,比方说不想要破坏到 callee-saved 寄存器)。 +- $C_{ij}$ 量化了 $t_i$ 和 $t_j$ 之间的相互影响 + - i === j? (不一定,比方说在传参的时候,i 可以放到 a0, 但是 j 是不能放到 a0 的) + - 但是我有一种想法: 就是我认为还是要把 32 个寄存器全部摆上台面的,如果不能用,我认为可以相应的可以设置为 $\infin$ ,这显然可能会变成一个 稀疏矩阵,然后是不是可以进行 凸优化啥的。如果 i!=j 并且有: $R_i$ ^ $R_j \ne \phi$ 那这可能需要对齐,可能有点麻烦 + - 如果 i === j,那么可以做一件事情:将 $c_{ij}$ 的对角线设置为 负数。对角线意味着: virtual reg $t_i$ 和 $t_j$ 是可接合的,可以分配到 同一个 physical reg 的,负数的 语义 是:鼓励接合(代价总是越小越好) +- $s_i = (\vec{cost(i)}, \vec0, ..., \vec0)$ + - $\vec{cost(t_i)}$ : $t_i$(virtual reg) 溢出的代价。如果每个 $t_i$ 都选择了 spill ,那么相当于是: $\forall t_i, X_i = (1, 0, ..., 0)$,那么 $\sum_{i=1}^n s_i X_i = \sum_{i=1}^n cost(t_i)$ + +## 伪代码 + +伪代码 ( from 华保健 ) 如下: + +```pseudocode +void reduce_1(vertex x) { + y = adj(x); + for (i = 1; i < len(E_y); i++) { + delta(i) = min(C_xy(i, :) + E_x); + } + E_y += delta; + remote_vertex(x); +} + +void reduce_2(vertex x) { + y, z = adj(x); + for (i = 1; i < len(E_y); i++) { + for (j = 1; j < len(E_z); j++) { + delta(i) = min(C_xy(i, :) + C_xz(j, :) + E_x); + } + } + C_yz += delta; + remote_vertex(x); +} + +void reduce_N(vertex x) { + y, z = adj(x); + for (i = 1; i < len(E_y); i++) { + delta(i) = 0; + for (j = 1; j < len(E_z); j++) { + delta(i) = min(C_xy(i, :) + E_y); + } + } + min_index = get_min_index(delta); + for y : vertex in adj(x) { + E_y += C_xy(min_index, :); + } + remote_vertex(x); +} + +void solve_PBQP(graph pbqp_model) { + while (pbqp_model still has edges) { + simplify(pbqp_model); + while (there exists any vertex x of degree 1) { + reduce_1(x); + } + while (there exists any vertex x of degree 2) { + reduce_2(x); + } + while (there exists any vertex x of degree >= 2) { + reduce_N(x); + } + } +} +``` + +### rust + +看 src/main.rs (from gpt) diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..06968be --- /dev/null +++ b/src/main.rs @@ -0,0 +1,125 @@ +use std::collections::{HashMap, HashSet}; + +struct Vertex { + cost_vector: Vec, // 代价向量 + neighbors: HashSet, // 邻接点索引集 +} + +struct Edge { + cost_matrix: Vec>, // 代价矩阵 +} + +struct Graph { + vertices: Vec, + edges: HashMap<(usize, usize), Edge>, +} + +impl Graph { + fn new() -> Self { + Self { + vertices: vec![], + edges: HashMap::new(), + } + } + + fn add_vertex(&mut self, cost_vector: Vec) -> usize { + let index = self.vertices.len(); + self.vertices.push(Vertex { + cost_vector, + neighbors: HashSet::new(), + }); + index + } + + fn add_edge(&mut self, v1: usize, v2: usize, cost_matrix: Vec>) { + self.vertices[v1].neighbors.insert(v2); + self.vertices[v2].neighbors.insert(v1); + self.edges.insert( + (v1, v2), + Edge { + cost_matrix: cost_matrix.clone(), + }, + ); + self.edges.insert((v2, v1), Edge { cost_matrix }); + } + + fn remove_vertex(&mut self, index: usize) { + if let Some(vertex) = self.vertices.get(index) { + for &neighbor in &vertex.neighbors { + self.vertices[neighbor].neighbors.remove(&index); + self.edges.remove(&(index, neighbor)); + self.edges.remove(&(neighbor, index)); + } + } + self.vertices.remove(index); + } + + // Function to simplify the graph based on degree of vertices + fn simplify(&mut self) { + while !self.edges.is_empty() { + for i in 0..self.vertices.len() { + match self.vertices[i].neighbors.len() { + 1 => self.reduce_1(i), + 2 => self.reduce_2(i), + _ => self.reduce_n(i), + } + } + } + } + + fn reduce_1(&mut self, x: usize) { + let y = *self.vertices[x].neighbors.iter().next().unwrap(); + let edge = self.edges.get(&(x, y)).unwrap(); + let mut delta = Vec::new(); + + for i in 0..self.vertices[y].cost_vector.len() { + let min_cost = edge.cost_matrix[i].iter().min().unwrap(); + delta.push(min_cost + self.vertices[x].cost_vector[i]); + } + + for i in 0..self.vertices[y].cost_vector.len() { + self.vertices[y].cost_vector[i] += delta[i]; + } + + self.remove_vertex(x); + } + + fn reduce_2(&mut self, x: usize) { + let mut iter = self.vertices[x].neighbors.iter(); + let y = *iter.next().unwrap(); + let z = *iter.next().unwrap(); + let mut delta = Vec::new(); + + for i in 0..self.vertices[y].cost_vector.len() { + for j in 0..self.vertices[z].cost_vector.len() { + let cost_y = self.edges.get(&(x, y)).unwrap().cost_matrix[i][j]; + let cost_z = self.edges.get(&(x, z)).unwrap().cost_matrix[i][j]; + delta.push(std::cmp::min(cost_y, cost_z) + self.vertices[x].cost_vector[i]); + } + } + + for i in 0..self.vertices[y].cost_vector.len() { + self.vertices[y].cost_vector[i] += delta[i]; + self.vertices[z].cost_vector[i] += delta[i]; + } + + self.remove_vertex(x); + } + + fn reduce_n(&mut self, x: usize) { + // This function needs to be implemented based on specific problem constraints and conditions + } +} + +fn main() { + let mut graph = Graph::new(); + // Example usage + let v0 = graph.add_vertex(vec![1, 2, 3]); + let v1 = graph.add_vertex(vec![4, 5, 6]); + let v2 = graph.add_vertex(vec![7, 8, 9]); + + graph.add_edge(v0, v1, vec![vec![10, 20], vec![30, 40]]); + graph.add_edge(v1, v2, vec![vec![50, 60], vec![70, 80]]); + + graph.simplify(); +}