Skip to content

Commit 95f5d74

Browse files
authored
Merge pull request #4614 from stacks-network/feat/tuple-perf-improvements
2.5: Tuple performance improvements
2 parents 132ced0 + aab1ffb commit 95f5d74

File tree

13 files changed

+222
-47
lines changed

13 files changed

+222
-47
lines changed

clarity/src/vm/analysis/contract_interface_builder/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,16 @@ impl ContractInterfaceAtomType {
171171
pub fn vec_from_tuple_type(
172172
tuple_type: &TupleTypeSignature,
173173
) -> Vec<ContractInterfaceTupleEntryType> {
174-
tuple_type
174+
let mut out: Vec<_> = tuple_type
175175
.get_type_map()
176176
.iter()
177177
.map(|(name, sig)| ContractInterfaceTupleEntryType {
178178
name: name.to_string(),
179179
type_f: Self::from_type_signature(sig),
180180
})
181-
.collect()
181+
.collect();
182+
out.sort_unstable_by(|ty1, ty2| ty1.name.cmp(&ty2.name));
183+
out
182184
}
183185

184186
pub fn from_type_signature(sig: &TypeSignature) -> ContractInterfaceAtomType {

clarity/src/vm/analysis/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,15 @@ pub fn mem_type_check(
7171
cost_tracker,
7272
epoch,
7373
version,
74+
true,
7475
) {
7576
Ok(x) => {
7677
// return the first type result of the type checker
7778
let first_type = x
7879
.type_map
7980
.as_ref()
8081
.ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?
81-
.get_type(
82+
.get_type_expected(
8283
x.expressions
8384
.last()
8485
.ok_or_else(|| CheckErrors::Expects("Should be non-empty".into()))?,
@@ -111,6 +112,7 @@ pub fn type_check(
111112
LimitedCostTracker::new_free(),
112113
*epoch,
113114
*version,
115+
true,
114116
)
115117
.map_err(|(e, _cost_tracker)| e)
116118
}
@@ -123,6 +125,7 @@ pub fn run_analysis(
123125
cost_tracker: LimitedCostTracker,
124126
epoch: StacksEpochId,
125127
version: ClarityVersion,
128+
build_type_map: bool,
126129
) -> Result<ContractAnalysis, (CheckError, LimitedCostTracker)> {
127130
let mut contract_analysis = ContractAnalysis::new(
128131
contract_identifier.clone(),
@@ -135,15 +138,15 @@ pub fn run_analysis(
135138
ReadOnlyChecker::run_pass(&epoch, &mut contract_analysis, db)?;
136139
match epoch {
137140
StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => {
138-
TypeChecker2_05::run_pass(&epoch, &mut contract_analysis, db)
141+
TypeChecker2_05::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
139142
}
140143
StacksEpochId::Epoch21
141144
| StacksEpochId::Epoch22
142145
| StacksEpochId::Epoch23
143146
| StacksEpochId::Epoch24
144147
| StacksEpochId::Epoch25
145148
| StacksEpochId::Epoch30 => {
146-
TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db)
149+
TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db, build_type_map)
147150
}
148151
StacksEpochId::Epoch10 => {
149152
return Err(CheckErrors::Expects(

clarity/src/vm/analysis/type_checker/contexts.rs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17+
use std::collections::HashSet;
18+
1719
use hashbrown::HashMap;
1820
use stacks_common::types::StacksEpochId;
1921

@@ -22,9 +24,22 @@ use crate::vm::types::signatures::CallableSubtype;
2224
use crate::vm::types::{TraitIdentifier, TypeSignature};
2325
use crate::vm::{ClarityName, ClarityVersion, SymbolicExpression, MAX_CONTEXT_DEPTH};
2426

25-
#[derive(Debug, PartialEq, Eq, Clone)]
27+
#[derive(Debug, Clone, PartialEq)]
2628
pub struct TypeMap {
27-
map: HashMap<u64, TypeSignature>,
29+
map: TypeMapDataType,
30+
}
31+
32+
#[derive(Debug, Clone, PartialEq)]
33+
/// This enum allows the type checker to operate
34+
/// with two different kinds of type maps. The Set
35+
/// version is more efficient, and only triggers an error
36+
/// if an AST node is visited more than once. The Map
37+
/// version is used when the actual type of each AST node
38+
/// is needed by a subsequent reader. This is only used by
39+
/// tests and docs generation.
40+
enum TypeMapDataType {
41+
Map(HashMap<u64, TypeSignature>),
42+
Set(HashSet<u64>),
2843
}
2944

3045
pub struct TypingContext<'a> {
@@ -36,33 +51,44 @@ pub struct TypingContext<'a> {
3651
pub depth: u16,
3752
}
3853

39-
impl Default for TypeMap {
40-
fn default() -> Self {
41-
Self::new()
42-
}
43-
}
44-
4554
impl TypeMap {
46-
pub fn new() -> TypeMap {
47-
TypeMap {
48-
map: HashMap::new(),
49-
}
55+
pub fn new(build_map: bool) -> TypeMap {
56+
let map = if build_map {
57+
TypeMapDataType::Map(HashMap::new())
58+
} else {
59+
TypeMapDataType::Set(HashSet::new())
60+
};
61+
TypeMap { map }
5062
}
5163

5264
pub fn set_type(
5365
&mut self,
5466
expr: &SymbolicExpression,
5567
type_sig: TypeSignature,
5668
) -> CheckResult<()> {
57-
if self.map.insert(expr.id, type_sig).is_some() {
58-
Err(CheckError::new(CheckErrors::TypeAlreadyAnnotatedFailure))
59-
} else {
60-
Ok(())
69+
match self.map {
70+
TypeMapDataType::Map(ref mut map) => {
71+
if map.insert(expr.id, type_sig).is_some() {
72+
Err(CheckError::new(CheckErrors::TypeAlreadyAnnotatedFailure))
73+
} else {
74+
Ok(())
75+
}
76+
}
77+
TypeMapDataType::Set(ref mut map) => {
78+
if !map.insert(expr.id) {
79+
Err(CheckError::new(CheckErrors::TypeAlreadyAnnotatedFailure))
80+
} else {
81+
Ok(())
82+
}
83+
}
6184
}
6285
}
6386

64-
pub fn get_type(&self, expr: &SymbolicExpression) -> Option<&TypeSignature> {
65-
self.map.get(&expr.id)
87+
pub fn get_type_expected(&self, expr: &SymbolicExpression) -> Option<&TypeSignature> {
88+
match self.map {
89+
TypeMapDataType::Map(ref map) => map.get(&expr.id),
90+
TypeMapDataType::Set(_) => None,
91+
}
6692
}
6793
}
6894

clarity/src/vm/analysis/type_checker/v2_05/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,15 @@ impl CostTracker for TypeChecker<'_, '_> {
111111
}
112112
}
113113

114-
impl AnalysisPass for TypeChecker<'_, '_> {
115-
fn run_pass(
114+
impl TypeChecker<'_, '_> {
115+
pub fn run_pass(
116116
_epoch: &StacksEpochId,
117117
contract_analysis: &mut ContractAnalysis,
118118
analysis_db: &mut AnalysisDatabase,
119+
build_type_map: bool,
119120
) -> CheckResult<()> {
120121
let cost_track = contract_analysis.take_contract_cost_tracker();
121-
let mut command = TypeChecker::new(analysis_db, cost_track);
122+
let mut command = TypeChecker::new(analysis_db, cost_track, build_type_map);
122123
// run the analysis, and replace the cost tracker whether or not the
123124
// analysis succeeded.
124125
match command.run(contract_analysis) {
@@ -343,13 +344,14 @@ impl<'a, 'b> TypeChecker<'a, 'b> {
343344
fn new(
344345
db: &'a mut AnalysisDatabase<'b>,
345346
cost_track: LimitedCostTracker,
347+
build_type_map: bool,
346348
) -> TypeChecker<'a, 'b> {
347349
Self {
348350
db,
349351
cost_track,
350352
contract_context: ContractContext::new(),
351353
function_return_tracker: None,
352-
type_map: TypeMap::new(),
354+
type_map: TypeMap::new(build_type_map),
353355
}
354356
}
355357

0 commit comments

Comments
 (0)