refactor(tvix/eval): wrap Builtin type in a Box
				
					
				
			This reduces the size of `Builtin` from 88 (!) bytes to 8, and as the
largest variant of `Value`, the size of that type from 96 to 64.
The next largest type is NixList, clocking in at 64 bytes.
This has noticeable performance impact. In an implementation without
disk I/O, evaluating nixpkgs.stdenv looks like this:
Benchmark 1: tvix -E '(import <nixpkgs> {}).stdenv.drvPath'
  Time (mean ± σ):      1.151 s ±  0.003 s    [User: 1.041 s, System: 0.109 s]
  Range (min … max):    1.147 s …  1.155 s    10 runs
After this change, it looks like this:
Benchmark 1: tvix -E '(import <nixpkgs> {}).stdenv.drvPath'
  Time (mean ± σ):      1.046 s ±  0.004 s    [User: 0.954 s, System: 0.092 s]
  Range (min … max):    1.041 s …  1.053 s    10 runs
Change-Id: I5ab7cc02a9a450c0227daf1f1f72966358311ebb
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8027
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
			
			
This commit is contained in:
		
							parent
							
								
									32698766ef
								
							
						
					
					
						commit
						f16b0f24e2
					
				
					 1 changed files with 30 additions and 20 deletions
				
			
		| 
						 | 
					@ -33,6 +33,19 @@ pub struct BuiltinArgument {
 | 
				
			||||||
    pub name: &'static str,
 | 
					    pub name: &'static str,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					pub struct BuiltinRepr {
 | 
				
			||||||
 | 
					    name: &'static str,
 | 
				
			||||||
 | 
					    /// Array of arguments to the builtin.
 | 
				
			||||||
 | 
					    arguments: &'static [BuiltinArgument],
 | 
				
			||||||
 | 
					    /// Optional documentation for the builtin.
 | 
				
			||||||
 | 
					    documentation: Option<&'static str>,
 | 
				
			||||||
 | 
					    func: Rc<dyn BuiltinFn>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Partially applied function arguments.
 | 
				
			||||||
 | 
					    partials: Vec<Value>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a single built-in function which directly executes Rust
 | 
					/// Represents a single built-in function which directly executes Rust
 | 
				
			||||||
/// code that operates on a Nix value.
 | 
					/// code that operates on a Nix value.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
| 
						 | 
					@ -46,16 +59,12 @@ pub struct BuiltinArgument {
 | 
				
			||||||
/// "capture" the partially applied arguments, and are treated
 | 
					/// "capture" the partially applied arguments, and are treated
 | 
				
			||||||
/// specially when printing their representation etc.
 | 
					/// specially when printing their representation etc.
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
pub struct Builtin {
 | 
					pub struct Builtin(Box<BuiltinRepr>);
 | 
				
			||||||
    name: &'static str,
 | 
					 | 
				
			||||||
    /// Array of arguments to the builtin.
 | 
					 | 
				
			||||||
    arguments: &'static [BuiltinArgument],
 | 
					 | 
				
			||||||
    /// Optional documentation for the builtin.
 | 
					 | 
				
			||||||
    documentation: Option<&'static str>,
 | 
					 | 
				
			||||||
    func: Rc<dyn BuiltinFn>,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Partially applied function arguments.
 | 
					impl From<BuiltinRepr> for Builtin {
 | 
				
			||||||
    partials: Vec<Value>,
 | 
					    fn from(value: BuiltinRepr) -> Self {
 | 
				
			||||||
 | 
					        Builtin(Box::new(value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Builtin {
 | 
					impl Builtin {
 | 
				
			||||||
| 
						 | 
					@ -65,36 +74,37 @@ impl Builtin {
 | 
				
			||||||
        documentation: Option<&'static str>,
 | 
					        documentation: Option<&'static str>,
 | 
				
			||||||
        func: F,
 | 
					        func: F,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Builtin {
 | 
					        BuiltinRepr {
 | 
				
			||||||
            name,
 | 
					            name,
 | 
				
			||||||
            arguments,
 | 
					            arguments,
 | 
				
			||||||
            documentation,
 | 
					            documentation,
 | 
				
			||||||
            func: Rc::new(func),
 | 
					            func: Rc::new(func),
 | 
				
			||||||
            partials: vec![],
 | 
					            partials: vec![],
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        .into()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn name(&self) -> &'static str {
 | 
					    pub fn name(&self) -> &'static str {
 | 
				
			||||||
        self.name
 | 
					        self.0.name
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn documentation(&self) -> Option<&'static str> {
 | 
					    pub fn documentation(&self) -> Option<&'static str> {
 | 
				
			||||||
        self.documentation
 | 
					        self.0.documentation
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Apply an additional argument to the builtin, which will either
 | 
					    /// Apply an additional argument to the builtin, which will either
 | 
				
			||||||
    /// lead to execution of the function or to returning a partial
 | 
					    /// lead to execution of the function or to returning a partial
 | 
				
			||||||
    /// builtin.
 | 
					    /// builtin.
 | 
				
			||||||
    pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> {
 | 
					    pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> {
 | 
				
			||||||
        self.partials.push(arg);
 | 
					        self.0.partials.push(arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.partials.len() == self.arguments.len() {
 | 
					        if self.0.partials.len() == self.0.arguments.len() {
 | 
				
			||||||
            for (idx, BuiltinArgument { strict, .. }) in self.arguments.iter().enumerate() {
 | 
					            for (idx, BuiltinArgument { strict, .. }) in self.0.arguments.iter().enumerate() {
 | 
				
			||||||
                if *strict {
 | 
					                if *strict {
 | 
				
			||||||
                    self.partials[idx].force(vm)?;
 | 
					                    self.0.partials[idx].force(vm)?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return (self.func)(self.partials, vm);
 | 
					            return (self.0.func)(self.0.partials, vm);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Function is not yet ready to be called.
 | 
					        // Function is not yet ready to be called.
 | 
				
			||||||
| 
						 | 
					@ -104,13 +114,13 @@ impl Builtin {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Debug for Builtin {
 | 
					impl Debug for Builtin {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        write!(f, "builtin[{}]", self.name)
 | 
					        write!(f, "builtin[{}]", self.0.name)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for Builtin {
 | 
					impl Display for Builtin {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        if !self.partials.is_empty() {
 | 
					        if !self.0.partials.is_empty() {
 | 
				
			||||||
            f.write_str("<<primop-app>>")
 | 
					            f.write_str("<<primop-app>>")
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            f.write_str("<<primop>>")
 | 
					            f.write_str("<<primop>>")
 | 
				
			||||||
| 
						 | 
					@ -121,6 +131,6 @@ impl Display for Builtin {
 | 
				
			||||||
/// Builtins are uniquely identified by their name
 | 
					/// Builtins are uniquely identified by their name
 | 
				
			||||||
impl PartialEq for Builtin {
 | 
					impl PartialEq for Builtin {
 | 
				
			||||||
    fn eq(&self, other: &Self) -> bool {
 | 
					    fn eq(&self, other: &Self) -> bool {
 | 
				
			||||||
        self.name == other.name
 | 
					        self.0.name == other.0.name
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue