git.delta.rocks / jrsonnet / refs/commits / fc45c6ca2bef

difftreelog

refactor turn Thunk structure around to reduce allocations

ztvvmqptYaroslav Bolyukin2026-02-12parent: #d5d04bb.patch.diff
in: master

9 files changed

modified.gitignorediffbeforeafterboth
4.vscode4.vscode
5.direnv5.direnv
66
7# Nix artifacts
8/result
9/result-*
10
7cache11cache
812
9jsonnet-cpp13jsonnet-cpp
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
7use super::ArrValue;7use super::ArrValue;
8use crate::{8use crate::{
9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,9 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,
10 Context, Error, ObjValue, Result, Thunk, Val,10 val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,
11};11};
1212
13pub trait ArrayLike: Any + Trace + Debug {13pub trait ArrayLike: Any + Trace + Debug {
191 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}191 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
192 };192 };
193193
194 let arr_thunk = self.clone();194 #[derive(Trace)]
195 struct ExprArrThunk {
196 expr: ExprArray,
197 index: usize,
198 }
199 impl ThunkValue for ExprArrThunk {
200 type Output = Val;
201
195 Some(Thunk!(move || {202 fn get(&self) -> Result<Self::Output> {
203 self.expr
196 arr_thunk.get(index).transpose().expect("index checked")204 .get(self.index)
205 .transpose()
206 .expect("index checked")
197 }))207 }
208 }
209
210 Some(Thunk::new(ExprArrThunk {
211 expr: self.clone(),
212 index,
213 }))
198 }214 }
199 fn get_cheap(&self, _index: usize) -> Option<Val> {215 fn get_cheap(&self, _index: usize) -> Option<Val> {
200 None216 None
484 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}500 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}
485 };501 };
486502
487 let arr_thunk = self.clone();503 #[derive(Trace)]
504 struct MappedArrayThunk<const WITH_INDEX: bool> {
505 arr: MappedArray<WITH_INDEX>,
506 index: usize,
507 }
508 impl<const WITH_INDEX: bool> ThunkValue for MappedArrayThunk<WITH_INDEX> {
509 type Output = Val;
510
488 Some(Thunk!(move || {511 fn get(&self) -> Result<Self::Output> {
489 arr_thunk.get(index).transpose().expect("index checked")512 self.arr.get(self.index).transpose().expect("index checked")
490 }))513 }
514 }
515
516 Some(Thunk::new(MappedArrayThunk {
517 arr: self.clone(),
518 index,
519 }))
491 }520 }
492521
493 fn get_cheap(&self, _index: usize) -> Option<Val> {522 fn get_cheap(&self, _index: usize) -> Option<Val> {
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
40impl<T: Trace + Clone> ThunkValue for Pending<T> {40impl<T: Trace + Clone> ThunkValue for Pending<T> {
41 type Output = T;41 type Output = T;
4242
43 fn get(self: Box<Self>) -> Result<Self::Output> {43 fn get(&self) -> Result<Self::Output> {
44 let Some(value) = self.0.get() else {44 let Some(value) = self.0.get() else {
45 // TODO: Other error?
45 bail!(InfiniteRecursionDetected);46 bail!(InfiniteRecursionDetected);
46 };47 };
47 Ok(value.clone())48 Ok(value.clone())
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
23 gc::WithCapacityExt as _,23 gc::WithCapacityExt as _,
24 identity_hash, in_frame,24 identity_hash, in_frame,
25 operator::evaluate_add_op,25 operator::evaluate_add_op,
26 val::ArrValue,26 val::{ArrValue, ThunkValue},
27 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,27 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,
28};28};
2929
753 if !self.has_field_ex(key.clone(), true) {753 if !self.has_field_ex(key.clone(), true) {
754 return None;754 return None;
755 }755 }
756 #[derive(Trace)]
757 struct ObjFieldThunk {
756 let obj = self.clone();758 obj: ObjValue,
759 key: IStr,
760 }
761 impl ThunkValue for ObjFieldThunk {
762 type Output = Val;
763
764 fn get(&self) -> Result<Self::Output> {
765 self.obj
766 .get(self.key.clone())
767 .transpose()
768 .expect("field existence checked")
769 }
770 }
757771
758 Some(Thunk!(move || Ok(obj.get(key)?.expect("field exists"))))772 Some(Thunk::new(ObjFieldThunk {
773 obj: self.clone(),
774 key,
775 }))
759 }776 }
760 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {777 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {
778 #[derive(Trace)]
779 struct ObjFieldThunk {
761 let obj = self.clone();780 obj: ObjValue,
781 key: IStr,
782 }
783 impl ThunkValue for ObjFieldThunk {
784 type Output = Val;
785
786 fn get(&self) -> Result<Self::Output> {
787 self.obj.get_or_bail(self.key.clone())
788 }
789 }
790
762 Thunk!(move || obj.get_or_bail(key))791 Thunk::new(ObjFieldThunk {
792 obj: self.clone(),
793 key,
794 })
763 }795 }
764 pub fn ptr_eq(a: &Self, b: &Self) -> bool {796 pub fn ptr_eq(a: &Self, b: &Self) -> bool {
765 Cc::ptr_eq(&a.0, &b.0)797 Cc::ptr_eq(&a.0, &b.0)
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
2 cell::RefCell,2 cell::RefCell,
3 cmp::Ordering,3 cmp::Ordering,
4 fmt::{self, Debug, Display},4 fmt::{self, Debug, Display},
5 marker::PhantomData,
5 mem::replace,6 mem::replace,
6 num::NonZeroU32,7 num::NonZeroU32,
7 ops::Deref,8 ops::Deref,
8 rc::Rc,9 rc::Rc,
9};10};
1011
11use jrsonnet_gcmodule::{Acyclic, Cc, Trace, TraceBox};12use jrsonnet_gcmodule::{cc_dyn, Acyclic, Cc, Trace};
12use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;
13pub use jrsonnet_macros::Thunk;14pub use jrsonnet_macros::Thunk;
14use jrsonnet_types::ValType;15use jrsonnet_types::ValType;
2829
29pub trait ThunkValue: Trace {30pub trait ThunkValue: Trace {
30 type Output;31 type Output;
31 fn get(self: Box<Self>) -> Result<Self::Output>;32 fn get(&self) -> Result<Self::Output>;
32}33}
3334
34#[derive(Trace)]35#[derive(Trace)]
35pub struct ThunkValueClosure<D: Trace, O: 'static> {36enum MemoizedClusureThunkInner<D: Trace, T: Trace> {
37 Computed(T),
38 Errored(Error),
39 Waiting {
36 env: D,40 env: D,
37 // Carries no data, as it is not a real closure, all the41 // Carries no data, as it is not a real closure, all the
38 // captured environment is stored in `env` field.42 // captured environment is stored in `env` field.
39 #[trace(skip)]43 #[trace(skip)]
40 closure: fn(D) -> Result<O>,44 closure: fn(D) -> Result<T>,
45 },
46 Pending,
41}47}
42impl<D: Trace, O: 'static> ThunkValueClosure<D, O> {
43 pub fn new(env: D, closure: fn(D) -> Result<O>) -> Self {
44 Self { env, closure }
45 }
46}
47impl<D: Trace, O: 'static> ThunkValue for ThunkValueClosure<D, O> {
48 type Output = O;
49
50 fn get(self: Box<Self>) -> Result<Self::Output> {
51 (self.closure)(self.env)
52 }
53}
54
55#[derive(Trace)]
56enum ThunkInner<T: Trace> {
57 Computed(T),
58 Errored(Error),
59 Waiting(TraceBox<dyn ThunkValue<Output = T>>),
60 Pending,
61}
62
63/// Lazily evaluated value
64#[allow(clippy::module_name_repetitions)]48#[derive(Trace)]
65#[derive(Clone, Trace)]
66pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);49pub struct MemoizedClosureThunk<D: Trace, T: Trace>(RefCell<MemoizedClusureThunkInner<D, T>>);
67
68impl<T: Trace> Thunk<T> {50impl<D: Trace, T: Trace> MemoizedClosureThunk<D, T> {
69 pub fn evaluated(val: T) -> Self {
70 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))
71 }
72 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {51 pub fn new(env: D, closure: fn(D) -> Result<T>) -> Self {
73 Self(Cc::new(RefCell::new(ThunkInner::Waiting(TraceBox(52 Self(RefCell::new(MemoizedClusureThunkInner::Waiting {
74 Box::new(f),53 env,
54 closure,
75 )))))55 }))
76 }56 }
77 pub fn errored(e: Error) -> Self {
78 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))
79 }
80 pub fn result(res: Result<T, Error>) -> Self {
81 match res {
82 Ok(o) => Self::evaluated(o),
83 Err(e) => Self::errored(e),
84 }
85 }
86}57}
8758
88impl<T> Thunk<T>59impl<D: Trace, T: Trace + Clone> ThunkValue for MemoizedClosureThunk<D, T> {
89where60 type Output = T;
90 T: Clone + Trace,61
91{
92 pub fn force(&self) -> Result<()> {
93 self.evaluate()?;
94 Ok(())
95 }
96
97 /// Evaluate thunk, or return cached value
98 ///
99 /// # Errors
100 ///
101 /// - Lazy value evaluation returned error
102 /// - This method was called during inner value evaluation
103 pub fn evaluate(&self) -> Result<T> {62 fn get(&self) -> Result<Self::Output> {
104 match &*self.0.borrow() {63 match &*self.0.borrow() {
105 ThunkInner::Computed(v) => return Ok(v.clone()),64 MemoizedClusureThunkInner::Computed(v) => return Ok(v.clone()),
106 ThunkInner::Errored(e) => return Err(e.clone()),65 MemoizedClusureThunkInner::Errored(e) => return Err(e.clone()),
107 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),66 MemoizedClusureThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),
108 ThunkInner::Waiting(..) => (),67 MemoizedClusureThunkInner::Waiting { .. } => (),
109 };68 };
110 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)69 let MemoizedClusureThunkInner::Waiting { env, closure } = replace(
70 &mut *self.0.borrow_mut(),
71 MemoizedClusureThunkInner::Pending,
111 else {72 ) else {
112 unreachable!();73 unreachable!();
113 };74 };
114 let new_value = match value.0.get() {75 let new_value = match closure(env) {
115 Ok(v) => v,76 Ok(v) => v,
116 Err(e) => {77 Err(e) => {
117 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());78 *self.0.borrow_mut() = MemoizedClusureThunkInner::Errored(e.clone());
118 return Err(e);79 return Err(e);
119 }80 }
120 };81 };
121 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());82 *self.0.borrow_mut() = MemoizedClusureThunkInner::Computed(new_value.clone());
122 Ok(new_value)83 Ok(new_value)
123 }84 }
124}85}
86
87cc_dyn!(
88 /// Lazily evaluated value
89 #[derive(Clone)] Thunk<V: Trace>,
90 ThunkValue<Output = V>,
91 pub fn new() {...}
92);
93
94impl<T: Trace> Thunk<T> {
95 pub fn evaluated(val: T) -> Self
96 where
97 T: Clone,
98 {
99 #[derive(Trace)]
100 struct EvaluatedThunk<T: Trace>(T);
101 impl<T> ThunkValue for EvaluatedThunk<T>
102 where
103 T: Clone + Trace,
104 {
105 type Output = T;
106
107 fn get(&self) -> Result<Self::Output> {
108 Ok(self.0.clone())
109 }
110 }
111 Self::new(EvaluatedThunk(val))
112 }
113 pub fn errored(e: Error) -> Self {
114 #[derive(Trace)]
115 struct ErroredThunk<T: Trace>(Error, PhantomData<T>);
116 impl<T> ThunkValue for ErroredThunk<T>
117 where
118 T: Trace,
119 {
120 type Output = T;
121
122 fn get(&self) -> Result<Self::Output> {
123 Err(self.0.clone())
124 }
125 }
126 Self::new(ErroredThunk(e, PhantomData))
127 }
128 pub fn result(res: Result<T, Error>) -> Self
129 where
130 T: Clone,
131 {
132 match res {
133 Ok(o) => Self::evaluated(o),
134 Err(e) => Self::errored(e),
135 }
136 }
137}
138
139impl<T> Thunk<T>
140where
141 T: Clone + Trace,
142{
143 pub fn force(&self) -> Result<()> {
144 self.evaluate()?;
145 Ok(())
146 }
147
148 /// Evaluate thunk, or return cached value
149 ///
150 /// # Errors
151 ///
152 /// - Lazy value evaluation returned error
153 /// - This method was called during inner value evaluation
154 pub fn evaluate(&self) -> Result<T> {
155 self.0.get()
156 }
157}
125158
126pub trait ThunkMapper<Input>: Trace {159pub trait ThunkMapper<Input>: Trace {
127 type Output;160 type Output;
134 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>167 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>
135 where168 where
136 M: ThunkMapper<Input>,169 M: ThunkMapper<Input>,
137 M::Output: Trace,170 M::Output: Trace + Clone,
138 {171 {
139 let inner = self;172 let inner = self;
140 Thunk!(move || {173 Thunk!(move || {
145 }178 }
146}179}
147180
148impl<T: Trace> From<Result<T>> for Thunk<T> {181impl<T: Trace + Clone> From<Result<T>> for Thunk<T> {
149 fn from(value: Result<T>) -> Self {182 fn from(value: Result<T>) -> Self {
150 match value {183 match value {
151 Ok(o) => Self::evaluated(o),184 Ok(o) => Self::evaluated(o),
162 }195 }
163}196}
164197
165impl<T: Trace + Default> Default for Thunk<T> {198impl<T: Trace + Default + Clone> Default for Thunk<T> {
166 fn default() -> Self {199 fn default() -> Self {
167 Self::evaluated(T::default())200 Self::evaluated(T::default())
168 }201 }
modifiedcrates/jrsonnet-formatter/src/comments.rsdiffbeforeafterboth

no syntactic changes

modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
879 quote! {{879 quote! {{
880 #move_check880 #move_check
881 #(#trace_check)*881 #(#trace_check)*
882 ::jrsonnet_evaluator::Thunk::new(::jrsonnet_evaluator::val::ThunkValueClosure::new(#env, #closure))882 ::jrsonnet_evaluator::Thunk::new(::jrsonnet_evaluator::val::MemoizedClosureThunk::new(#env, #closure))
883 }}.into()883 }}.into()
884}884}
885885
deletedresultdiffbeforeafterboth

no changes

modifiedxtask/src/sourcegen/mod.rsdiffbeforeafterboth

no syntactic changes