mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 03:51:08 +00:00
STEP import: a part's later instances in a window wait for its first, not end the window
A window of planned solids ended at the second instance of a part not yet read, so an assembly that places a part several times in a row converted in windows of one or two solids, each waiting for its slowest read before the next began (Poppy Humanoid: 14 windows of 1 to 30 reads on 8 workers). The window now always takes MAX_WINDOW solids; an instance of a part an earlier job of the window reads is placed after that job, from the orientation it found, as the next window would have placed it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -764,27 +764,24 @@ impl Reader {
|
||||
let first = self.converted;
|
||||
// The next solids, across instances and parts, converted together: parts still to
|
||||
// orient are shared out between the workers. A part that comes back within the window
|
||||
// waits for the next one, which places the orientation its first instance found.
|
||||
let mut last = first + 1;
|
||||
if !single {
|
||||
let mut fresh: HashSet<u64> = self.plan.get(first).map(|p| p.item).into_iter().collect();
|
||||
while let Some(p) = self.plan.get(last)
|
||||
&& last - first < MAX_WINDOW
|
||||
{
|
||||
if !self.oriented.contains_key(&p.item) && !fresh.insert(p.item) {
|
||||
break;
|
||||
}
|
||||
last += 1;
|
||||
}
|
||||
}
|
||||
// is placed once its first instance is read, from the orientation that instance found.
|
||||
let last = if single { first + 1 } else { (first + MAX_WINDOW).min(self.plan.len()) };
|
||||
self.converted = last;
|
||||
let ex = self.ex.clone();
|
||||
// Each planned solid of the window, with its job (`None`: a later instance of a part an
|
||||
// earlier job of the window reads, placed after it).
|
||||
let mut sources = Vec::new();
|
||||
let mut jobs = Vec::new();
|
||||
let mut reading = HashSet::new();
|
||||
for i in first..last {
|
||||
self.made.push(None);
|
||||
let Some(p) = self.plan.get(i) else { continue };
|
||||
let (item, units) = (p.item, p.units);
|
||||
let placement = self.batches.get(p.batch).map_or(IDENTITY, |b| b.m);
|
||||
if reading.contains(&item) {
|
||||
sources.push((i, item, placement, None));
|
||||
continue;
|
||||
}
|
||||
if let Some(again) = self.unreadable.get(&item) {
|
||||
if let Some(msg) = again.clone() {
|
||||
let label = self.label(item);
|
||||
@@ -794,15 +791,42 @@ impl Reader {
|
||||
}
|
||||
let source = match self.oriented.get(&item) {
|
||||
Some(part) => parallel::Source::Oriented(part.clone()),
|
||||
None => parallel::Source::Item { item, units },
|
||||
None => {
|
||||
reading.insert(item);
|
||||
parallel::Source::Item { item, units }
|
||||
}
|
||||
};
|
||||
let placement = self.batches.get(p.batch).map_or(IDENTITY, |b| b.m);
|
||||
sources.push((i, item));
|
||||
sources.push((i, item, placement, Some(jobs.len())));
|
||||
jobs.push(parallel::Job { source, placement, check_mesh: !self.unmeshed_checked.contains(&item) });
|
||||
}
|
||||
// Reading and orienting new parts is shared out between the workers.
|
||||
let prepared = parallel::bodies(&ex, &jobs, self.workers);
|
||||
for ((i, item), made) in sources.into_iter().zip(prepared) {
|
||||
let mut prepared: Vec<Option<_>> = parallel::bodies(&ex, &jobs, self.workers).into_iter().map(Some).collect();
|
||||
for (i, item, placement, job) in sources {
|
||||
let made = match job {
|
||||
Some(j) => match prepared.get_mut(j).and_then(Option::take) {
|
||||
Some(made) => made,
|
||||
None => continue,
|
||||
},
|
||||
// A later instance of a part read in this window: as it would be in the next
|
||||
// window, after its first instance.
|
||||
None => {
|
||||
if let Some(again) = self.unreadable.get(&item) {
|
||||
if let Some(msg) = again.clone() {
|
||||
let label = self.label(item);
|
||||
self.warn(format!("{label}: not imported: {msg}"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let source = match self.oriented.get(&item) {
|
||||
Some(part) => parallel::Source::Oriented(part.clone()),
|
||||
None => match self.plan.get(i) {
|
||||
Some(p) => parallel::Source::Item { item, units: p.units },
|
||||
None => continue,
|
||||
},
|
||||
};
|
||||
parallel::prepare(&ex, ¶llel::Job { source, placement, check_mesh: !self.unmeshed_checked.contains(&item) })
|
||||
}
|
||||
};
|
||||
let Some(e) = ex.get(item) else { continue };
|
||||
let Some(batch) = self.plan.get(i).and_then(|p| self.batches.get(p.batch)) else { continue };
|
||||
let (m, path, product, instance) = (batch.m, batch.path.clone(), batch.product.clone(), batch.instance.clone());
|
||||
|
||||
@@ -70,7 +70,7 @@ pub(super) fn workers() -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(ex: &Exchange, job: &Job) -> Prepared {
|
||||
pub(super) fn prepare(ex: &Exchange, job: &Job) -> Prepared {
|
||||
let _span = solidcraft_profile::span("import", "step.body");
|
||||
let (part, read) = match &job.source {
|
||||
Source::Oriented(part) => (part.clone(), None),
|
||||
|
||||
@@ -772,9 +772,9 @@ fn instanced_parts_are_oriented_once() {
|
||||
}
|
||||
|
||||
/// Distinct parts are oriented together, not one occurrence at a time: an assembly placing
|
||||
/// two parts in turn converts in a few steps of many solids (the first finds both parts'
|
||||
/// orientations side by side on the workers, the next places every other instance), where
|
||||
/// converting instance by instance took one step each.
|
||||
/// two parts in turn converts in one step (both parts' orientations found side by side on the
|
||||
/// workers, every other instance placed after its part's first), where converting instance by
|
||||
/// instance took one step each, and stopping the step at a part's second instance two.
|
||||
#[test]
|
||||
fn distinct_parts_convert_together_across_instances() {
|
||||
let n = 24;
|
||||
@@ -784,7 +784,7 @@ fn distinct_parts_convert_together_across_instances() {
|
||||
while reading.step() {
|
||||
steps += 1;
|
||||
}
|
||||
assert_eq!(steps, 2, "both parts in the first step, every other instance in the second");
|
||||
assert_eq!(steps, 1, "both parts and every other instance in one step");
|
||||
let threaded = reading.finish().unwrap();
|
||||
assert_eq!(threaded.bodies.len(), n as usize);
|
||||
assert!(threaded.warnings.is_empty(), "{:?}", threaded.warnings);
|
||||
|
||||
Reference in New Issue
Block a user