This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/release-note/upgrading-to-3.2/performance-and-footprint.html
T
2026-09-13 19:45:24 +08:00

359 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Performance and footprint | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Performance and footprint | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Release-Upgrading-Performance">
<h1 id="performance-and-footprint">Performance and footprint</h1>
<p>Every figure below is a measurement with its conditions stated, and where a change cost time to buy
correctness that is said too. Read the ratios rather than the absolute times: several campaigns were
run on Debug builds or on small machines, deliberately, because a paired A/B on one machine answers
&ldquo;did this get faster&rdquo; far more reliably than an unpaired Release number on a fast one.</p>
<h2 id="against-the-last-31-set">Against the last 3.1 set</h2>
<p>Paired on one 32-logical-processor Linux workstation, deployed stacks, <strong>whole</strong> NC programs, steps per
second. The 3.1 side is HardNc (the 3.1 default); the 3.2 side is SoftNc (the 3.2 default). Throughput
on this line is <strong>2.5×–18.8×</strong>; the ratio grows with program length and mesh fineness. Memory is mixed:
a cutting-dominated long program peaked higher on 3.2, a large-surface short-NC job peaked lower.</p>
<p>The six-core ceiling that used to cap the 3.1 pipeline is <strong>not</strong> where that gap comes from. On the
same workstation, pinning 3.2's worker counts to the products of that ceiling moved a ~193,000-step
play from 15.0 s to 15.9 s (+6%); at six available cores the version advantage is still 2.8×–3.8×,
and at two cores 2.73×. 3.1 saturates from eight cores; 3.2 from twelve. On that fixture, 3.1 allocated
6–8× more managed memory, and the 3.1 allocation climbed as the mesh got finer while 3.2 stayed
almost flat.</p>
<blockquote>
<p><strong>Conditions.</strong> Deployed x64 stacks, one machine, whole programs, steps/s (not wall-clock — a
defect that inflated 3.2 step counts on an earlier drop is fixed on this line). Core-count figures
used <code>DOTNET_PROCESSOR_COUNT</code> plus CPU affinity, so they are &ldquo;restricted cores on a large machine&rdquo;,
not a small one: memory bandwidth and cache stay those of the 32-LP host.</p>
</blockquote>
<h2 id="milling-physics-in-the-native-kernel">Milling physics in the native kernel</h2>
<p>The per-step physics moved into <code>core.dll</code> in stages — engagement scan conversion, the force kernel,
then the sequential temperature and wear chain. Measured as a same-day paired A/B, managed leg
against native leg, on one circular test program:</p>
<table>
<thead>
<tr>
<th>Per touched step</th>
<th>managed</th>
<th>native</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Engagement build</td>
<td>46.0 ms</td>
<td><strong>5.03 ms</strong></td>
<td>~9×</td>
</tr>
<tr>
<td>Force (<code>GetMillingFoce</code>)</td>
<td>17.1 ms</td>
<td><strong>0.75 ms</strong></td>
<td>~20×</td>
</tr>
<tr>
<td>Temperature and wear chain</td>
<td>3.97 ms</td>
<td><strong>0.24 ms</strong></td>
<td>~16×</td>
</tr>
<tr>
<td><strong>Whole play</strong></td>
<td><strong>59.3 s / 16.1 GB allocated</strong></td>
<td><strong>40.6 s / 3.4 GB allocated</strong></td>
<td>−32% wall, −79% allocation</td>
</tr>
</tbody>
</table>
<p>Allocation is where the migration bites hardest: engagement construction alone fell from about
5.2 GB to 29 MB per step.</p>
<blockquote>
<p><strong>Conditions.</strong> Debug x64, <code>EnablePhysics</code> on, collision off, one force worker, a two-core /
four-thread laptop, ±10% thermal-throttle noise, single paired run per leg. Absolute times are not
representative of a customer machine — the ratios are the claim.</p>
</blockquote>
<blockquote>
<p><strong>Numerical parity.</strong> Engagement, force and brief are bit-identical between the two legs <strong>on
Windows</strong>. The Linux build is not bit-identical (different libm), so a cross-platform comparison
should use a tolerance, not equality.</p>
</blockquote>
<h2 id="playing-a-long-program-stays-linear">Playing a long program stays linear</h2>
<p><code>NcOptOption.Equals</code> ended on a null-propagating comparison of a dictionary that is created on
demand and is null on virtually every option, so the whole comparison collapsed to <code>false</code> — an
option compared unequal even to a copy of itself. Both record-on-change guards built on it were
therefore dead: the session appended an option-map entry for <strong>every played act</strong> instead of only at
change points, and the step rewrote unchanged entries. Reading the last recorded option through a
LINQ <code>LastOrDefault</code> over a <code>SortedList&lt;,&gt;</code> — which has no indexed fast path — then walked the whole
map each time, so the two defects together made a long play quadratic.</p>
<blockquote>
<p>Measured on a 2.35-million-line Siemens program: the option map now holds <strong>1 entry instead of one
per line</strong>, and the per-100,000-line rate stays <strong>flat</strong> instead of degrading from 73 s at the
start of the file to about 11 minutes by 1.9 million lines.</p>
</blockquote>
<p>This applies to <strong>every</strong> runner and to sessions doing no optimization at all, because the call site
is the session-level play loop. <code>GetHashCode</code> drops the dictionary in the same change, since it
hashed by reference and would otherwise disagree with <code>Equals</code> — relevant if you use <code>NcOptOption</code>
as a dictionary key.</p>
<h2 id="where-the-time-actually-goes">Where the time actually goes</h2>
<p>Worth knowing before you tune anything. After the migration, on the measured workload the <strong>whole
parallel physics stage is about 3.3% of wall time</strong>, while the <strong>single-worker volume subtraction is
about 77%</strong> — and that subtraction is single-worker as a correctness requirement, not as an
oversight. Raising the force-worker count therefore buys nothing on any machine; the bottleneck
moved rather than disappearing.</p>
<p>What did change in the worker derivation is narrower than it sounds. The six-core ceiling
was removed, and on a 32-logical-processor machine that is about <strong>6%</strong> of the 3.1→3.2 wall-time gap
measured above — force workers are unchanged everywhere, and the sweep-worker count only differs on
machines with <strong>nine or more</strong> logical processors. The rest of the gap is allocation and the rest of
the pipeline, not the extra cores.</p>
<p>Queue depths became fixed item budgets (120 geometry, 3840 physics) rather than scaling with the core
count, because those queues bound per-item memory: uncapped, a 64-core machine would have been handed
40,960-deep physics queues. On machines with fewer than six cores this is a small <em>increase</em> in
bounded-queue memory (from 80 / 2560), which is the honest cost of the change.</p>
<h2 id="loading-a-large-stl-workpiece">Loading a large STL workpiece</h2>
<p>Building the topology from an STL was quadratic in triangle count — a pointer-derived hash collapsed
into a handful of buckets, so lookups degenerated into linear scans. On one 935,000-triangle binary
STL, topology construction was <strong>99.6%</strong> of the entire load; reading the file off disk was 0.16% of
it. With a multiplicative hash mix the build is linear:</p>
<table>
<thead>
<tr>
<th>Triangles</th>
<th>before</th>
<th>after</th>
<th></th>
<th>worst bucket</th>
</tr>
</thead>
<tbody>
<tr>
<td>100,000</td>
<td>8,711 ms</td>
<td><strong>1,305 ms</strong></td>
<td>6.7×</td>
<td>2,245 → 21</td>
</tr>
<tr>
<td>300,000</td>
<td>89,036 ms</td>
<td><strong>4,750 ms</strong></td>
<td>18.7×</td>
<td>6,593 → 23</td>
</tr>
</tbody>
</table>
<p>Per-triangle cost is now flat (0.013 → 0.016 ms/tri across a 3× size increase), which is the real
result: the cost grows with the mesh instead of with its square. Extrapolated to the full
935,000-triangle mesh that is roughly <strong>14 minutes → 15 seconds</strong>.</p>
<p>Deduplication and the resulting topology are unchanged — the equality predicate was not touched, and
the 300,000-triangle case produces an identical 899,997 lines before and after. Separately, the
managed-to-native STL handoff dropped from three full copies of the buffer (about 86 MB each, plus
around twenty doubling reallocations) to two.</p>
<blockquote>
<p><strong>Conditions.</strong> Native test harness, debug CRT — which inflates container-operation constants, so
the absolute milliseconds are an upper bound. The composition breakdown and the complexity change
are build-configuration independent. The full-mesh figure is an extrapolation, not a measured run.</p>
</blockquote>
<h2 id="re-triangulating-after-a-cut">Re-triangulating after a cut</h2>
<p>The marching-cubes step gained a lookup table, and produces fewer triangles for the same surface:</p>
<table>
<thead>
<tr>
<th>Tree</th>
<th>before</th>
<th>after</th>
<th></th>
<th>triangles</th>
</tr>
</thead>
<tbody>
<tr>
<td>17 MB diagnostic</td>
<td>0.63 s</td>
<td>0.29 s</td>
<td>2.17×</td>
<td>−35%</td>
</tr>
<tr>
<td>30 MB demo</td>
<td>1.29 s</td>
<td>0.60 s</td>
<td>2.15×</td>
<td>−35%</td>
</tr>
<tr>
<td><strong>309 MB customer part</strong></td>
<td><strong>14.68 s</strong></td>
<td><strong>6.51 s</strong></td>
<td><strong>2.25×</strong></td>
<td><strong>−43%</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Scope.</strong> This lands on the rebuild burst after a cut invalidates cached geometry, <strong>not</strong> on
steady-state rendering, which draws from the display cache and is unchanged.</p>
</blockquote>
<p>It is also an approximation change, not purely a speedup: a non-finite cut drops its triangles, so a
sub-voxel feature vanishes at that level of detail instead of being capped. That is what fixed the
broken-face slabs seen on RTCP paths. The contact-loop extraction used by milling physics
deliberately still uses the previous walk, so physics results are untouched.</p>
<h2 id="session-memory-a-long-program-no-longer-exhausts-the-client">Session memory: a long program no longer exhausts the client</h2>
<p>A session retains every executed NC block for its whole lifetime. Once a block leaves the executing
window its piece is now frozen to compact UTF-8.</p>
<blockquote>
<p>Measured on a 25,018-block play: <strong>session retention 406 MB → 142 MB</strong>, about 2.9×.</p>
</blockquote>
<p>The encoding itself is smaller than that ratio suggests — roughly 12 KB per line live against 1.6 KB
frozen, about 12.9× — because a retained piece carries more than its JSON. The 2.9× is the figure
that matters for whether a program fits in memory.</p>
<p>The trade is explicit: after the freeze the <code>JsonObject</code> getter re-parses on every call and returns a
fresh read-only snapshot, with no caching and no write-back. Code that reads the same piece
repeatedly should hold the snapshot in a local. The switch is
<a class="xref" href="../../api/Hi.MachiningProcs.NcRunnerSessionState.html#Hi_MachiningProcs_NcRunnerSessionState_FreezeExecutedPieces">FreezeExecutedPieces</a>, on by default.</p>
<h2 id="sizing-a-meshed-workpiece">Sizing a meshed workpiece</h2>
<p>A cube tree costs roughly <strong>3.2× its file size in RAM</strong> while loaded — a 10 GB <code>.wct</code> at 0.125 mm
resolution is about 95 million nodes, holding around 25 GB of tree plus 6 GB of index. Tearing down a
tree that size used to block the caller for over two minutes; disposal now runs serialized on a
background chain, so the thread that dropped it does not wait. The remaining cost is genuine work:
the live renderer must not be left showing geometry that no longer exists.</p>
<h2 id="cutter-location-files-at-production-scale">Cutter-location files at production scale</h2>
<p>Three costs were removed from the CL-to-machine path, and on a production-scale file they are the
difference between replaying and appearing to hang: tool-offset resolution walked back to the distant
<code>LOAD</code> block for every motion (O(N²), now O(1) through a modal section), the documentary
program-to-Pn stamp did the same walk (now stamped once per run), and the program-zero query
deep-cloned the whole equipment assembly on every motion block (now a cached per-run matrix over the
live assembly). These are complexity changes; they have not been separately timed.</p>
<h2 id="smaller-footprint">Smaller footprint</h2>
<p>The embedded default font is handed to the display engine from memory, so an <strong>11 MB</strong> <code>.ttf</code> is no
longer written into the process working directory on startup. The packages are x64-only, and
HiNc-Resource no longer ships a duplicated nested copy of the CT-350 STL set.</p>
<h2 id="things-that-cost-more-on-purpose">Things that cost more, on purpose</h2>
<p><strong>Five-axis inverse kinematics.</strong> Tightening the orientation envelope from about 1.4e-3 rad to about
1e-6 rad — measured maxima 1.5e-8 rad on the hot path and 2.1e-8 on teleport, a tip deviation of
0.05 µm on a 50 mm tool — costs roughly eleven solver iterations where one used to do, so a
posture-changing call went from about 23 µs to about 251 µs, and a teleport from 418 µs to 1607 µs.
Only posture-changing RTCP and arc steps pay it: three-axis programs and constant-posture segments
are exempt through the <code>McLinear</code> downgrade. In absolute terms a 1,432-step five-axis replay spends
about 0.36 s in the solver. (Debug build including measurement overhead, so those microseconds are an
upper bound.)</p>
<p><strong>Machine-coordinate linear stepping.</strong> <a class="xref" href="../../api/Hi.Numerical.Acts.ActMcXyzLinearContour.html">ActMcXyzLinearContour</a> derives its
step count from the <strong>euclidean</strong> length of the machine XYZ delta rather than the largest single-axis
component, so <code>LinearResolution_mm</code> caps actual tool-tip travel per step. A diagonal move therefore
produces up to √3× more steps than before at the same setting — more work, for a sampling density
that now means what the setting says. Lower the resolution if the old step count was what you were
budgeting for.</p>
<h2 id="a-tuning-cliff-worth-knowing-about">A tuning cliff worth knowing about</h2>
<p><code>MillingCycleDivisionNum</code> saturates. Raising it past roughly 180 buys no additional training accuracy
while the cost keeps climbing: a training run that takes about four minutes at 180 takes hours at 720
and needs on the order of 100 GB of RAM to do it. The default of 36 is for ordinary simulation; raise
it for training, but not past the point where the curve flattens.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>