Skip to content

Commit

Permalink
Use OnceLock instead of OnceCell
Browse files Browse the repository at this point in the history
OnceLock is a thread-safe version of OnceCell that enables us to use
PackedInstruction from a threaded environment. There is some overhead
associated with this, primarily in memory as the OnceLock is a larger
type than a OnceCell. But the tradeoff is worth it to start leverage
multithreading for circuits.

Fixes #13219
  • Loading branch information
mtreinish committed Nov 8, 2024
1 parent 4fd2a4e commit 610cf94
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 31 deletions.
4 changes: 2 additions & 2 deletions crates/accelerate/src/unitary_synthesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#![allow(clippy::too_many_arguments)]

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;
use std::f64::consts::PI;

use approx::relative_eq;
Expand Down Expand Up @@ -149,7 +149,7 @@ fn apply_synth_sequence(
params: new_params,
extra_attrs: ExtraInstructionAttributes::default(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
};
instructions.push(instruction);
}
Expand Down
12 changes: 6 additions & 6 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// that they have been altered from the originals.

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;

use crate::bit_data::BitData;
use crate::circuit_instruction::{
Expand Down Expand Up @@ -302,7 +302,7 @@ impl CircuitData {
params: inst.params.clone(),
extra_attrs: inst.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
});
}
} else if copy_instructions {
Expand All @@ -314,7 +314,7 @@ impl CircuitData {
params: inst.params.clone(),
extra_attrs: inst.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
});
}
} else {
Expand Down Expand Up @@ -939,7 +939,7 @@ impl CircuitData {
params,
extra_attrs: ExtraInstructionAttributes::default(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
});
res.track_instruction_parameters(py, res.data.len() - 1)?;
}
Expand Down Expand Up @@ -1048,7 +1048,7 @@ impl CircuitData {
params,
extra_attrs: ExtraInstructionAttributes::default(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
});
res.track_instruction_parameters(py, res.data.len() - 1)?;
}
Expand Down Expand Up @@ -1106,7 +1106,7 @@ impl CircuitData {
params,
extra_attrs: ExtraInstructionAttributes::default(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
});
Ok(())
}
Expand Down
6 changes: 3 additions & 3 deletions crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// that they have been altered from the originals.

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;

use numpy::IntoPyArray;
use pyo3::basic::CompareOp;
Expand Down Expand Up @@ -236,7 +236,7 @@ pub struct CircuitInstruction {
pub params: SmallVec<[Param; 3]>,
pub extra_attrs: ExtraInstructionAttributes,
#[cfg(feature = "cache_pygates")]
pub py_op: OnceCell<Py<PyAny>>,
pub py_op: OnceLock<Py<PyAny>>,
}

impl CircuitInstruction {
Expand Down Expand Up @@ -301,7 +301,7 @@ impl CircuitInstruction {
params,
extra_attrs: ExtraInstructionAttributes::new(label, None, None, None),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
})
}

Expand Down
4 changes: 2 additions & 2 deletions crates/circuit/src/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// that they have been altered from the originals.

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;

use ::pyo3::prelude::*;
use hashbrown::HashMap;
Expand Down Expand Up @@ -126,7 +126,7 @@ pub fn dag_to_circuit(
)),
extra_attrs: instr.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
})
} else {
Ok(instr.clone())
Expand Down
18 changes: 9 additions & 9 deletions crates/circuit/src/dag_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ use std::convert::Infallible;
use std::f64::consts::PI;

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;

static CONTROL_FLOW_OP_NAMES: [&str; 4] = ["for_loop", "while_loop", "if_else", "switch_case"];
static SEMANTIC_EQ_SYMMETRIC: [&str; 4] = ["barrier", "swap", "break_loop", "continue_loop"];
Expand Down Expand Up @@ -5147,7 +5147,7 @@ impl DAGCircuit {
let py_op = if let Some(py_op) = py_op {
py_op.into()
} else {
OnceCell::new()
OnceLock::new()
};
let packed_instruction = PackedInstruction {
op,
Expand Down Expand Up @@ -6193,7 +6193,7 @@ impl DAGCircuit {
.then(|| Box::new(new_gate.1.iter().map(|x| Param::Float(*x)).collect())),
extra_attrs: ExtraInstructionAttributes::default(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
}
} else {
panic!("This method only works if provided index is an op node");
Expand Down Expand Up @@ -6276,12 +6276,12 @@ impl DAGCircuit {
};
#[cfg(feature = "cache_pygates")]
let py_op = match new_op.operation.view() {
OperationRef::Standard(_) => OnceCell::new(),
OperationRef::Gate(gate) => OnceCell::from(gate.gate.clone_ref(py)),
OperationRef::Standard(_) => OnceLock::new(),
OperationRef::Gate(gate) => OnceLock::from(gate.gate.clone_ref(py)),
OperationRef::Instruction(instruction) => {
OnceCell::from(instruction.instruction.clone_ref(py))
OnceLock::from(instruction.instruction.clone_ref(py))
}
OperationRef::Operation(op) => OnceCell::from(op.operation.clone_ref(py)),
OperationRef::Operation(op) => OnceLock::from(op.operation.clone_ref(py)),
};
let inst = PackedInstruction {
op: new_op.operation,
Expand Down Expand Up @@ -6732,7 +6732,7 @@ impl DAGCircuit {
params: instr.params.clone(),
extra_attrs: instr.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
})
})
.collect::<PyResult<Vec<_>>>()?;
Expand Down Expand Up @@ -6994,7 +6994,7 @@ impl DAGCircuit {
params: (!new_op.params.is_empty()).then(|| new_op.params.into()),
extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op: py_op_cache.map(OnceCell::from).unwrap_or_default(),
py_op: py_op_cache.map(OnceLock::from).unwrap_or_default(),
});
if let Some(weight) = self.dag.node_weight_mut(node_index) {
*weight = new_weight;
Expand Down
6 changes: 3 additions & 3 deletions crates/circuit/src/dag_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// that they have been altered from the originals.

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;
use std::hash::Hasher;

use crate::circuit_instruction::{CircuitInstruction, OperationFromPython};
Expand Down Expand Up @@ -241,7 +241,7 @@ impl DAGOpNode {
instruction.operation = instruction.operation.py_deepcopy(py, None)?;
#[cfg(feature = "cache_pygates")]
{
instruction.py_op = OnceCell::new();
instruction.py_op = OnceLock::new();
}
}
let base = PyClassInitializer::from(DAGNode { node: None });
Expand Down Expand Up @@ -293,7 +293,7 @@ impl DAGOpNode {
params: self.instruction.params.clone(),
extra_attrs: self.instruction.extra_attrs.clone(),
#[cfg(feature = "cache_pygates")]
py_op: OnceCell::new(),
py_op: OnceLock::new(),
})
}

Expand Down
12 changes: 6 additions & 6 deletions crates/circuit/src/packed_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// that they have been altered from the originals.

#[cfg(feature = "cache_pygates")]
use std::cell::OnceCell;
use std::sync::OnceLock;
use std::ptr::NonNull;

use pyo3::intern;
Expand Down Expand Up @@ -504,17 +504,17 @@ pub struct PackedInstruction {
pub extra_attrs: ExtraInstructionAttributes,

#[cfg(feature = "cache_pygates")]
/// This is hidden in a `OnceCell` because it's just an on-demand cache; we don't create this
/// unless asked for it. A `OnceCell` of a non-null pointer type (like `Py<T>`) is the same
/// This is hidden in a `OnceLock` because it's just an on-demand cache; we don't create this
/// unless asked for it. A `OnceLock` of a non-null pointer type (like `Py<T>`) is the same
/// size as a pointer and there are no runtime checks on access beyond the initialisation check,
/// which is a simple null-pointer check.
///
/// WARNING: remember that `OnceCell`'s `get_or_init` method is no-reentrant, so the initialiser
/// WARNING: remember that `OnceLock`'s `get_or_init` method is no-reentrant, so the initialiser
/// must not yield the GIL to Python space. We avoid using `GILOnceCell` here because it
/// requires the GIL to even `get` (of course!), which makes implementing `Clone` hard for us.
/// We can revisit once we're on PyO3 0.22+ and have been able to disable its `py-clone`
/// feature.
pub py_op: OnceCell<Py<PyAny>>,
pub py_op: OnceLock<Py<PyAny>>,
}

impl PackedInstruction {
Expand Down Expand Up @@ -581,7 +581,7 @@ impl PackedInstruction {
}
};

// `OnceCell::get_or_init` and the non-stabilised `get_or_try_init`, which would otherwise
// `OnceLock::get_or_init` and the non-stabilised `get_or_try_init`, which would otherwise
// be nice here are both non-reentrant. This is a problem if the init yields control to the
// Python interpreter as this one does, since that can allow CPython to freeze the thread
// and for another to attempt the initialisation.
Expand Down

0 comments on commit 610cf94

Please sign in to comment.