matrix thread pool use ParaWaveFront and heavy sync

This commit is contained in:
Youmu Liu
2026-04-26 17:38:10 +08:00
parent 251762791e
commit ecb1f04770
7 changed files with 152 additions and 131 deletions
@@ -1,30 +1,27 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="NotEnumerableList.cs"
// file="LinearBag"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -41,7 +38,9 @@ namespace StgSharp.Collections
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
/// the input.
/// </summary>
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
public static LinearBag<T> Create<T>(
ReadOnlySpan<T> values
)
{
if (values.IsEmpty) {
return new LinearBag<T>(4);
@@ -63,8 +62,10 @@ namespace StgSharp.Collections
/// <typeparam name="T">
/// Element type.
/// </typeparam>
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
public class LinearBag<T>(int capacity = 4)
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.Create))]
public class LinearBag<T>(
int capacity = 4
)
{
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
@@ -73,17 +74,20 @@ namespace StgSharp.Collections
/// <summary>
/// Access element by index (0..Count-1). Throws if out of range.
/// </summary>
public T this[int index]
public T this[
int index
]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
get
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Count);
return _items[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
}
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Count);
_items[index] = value;
}
}
@@ -102,7 +106,9 @@ namespace StgSharp.Collections
/// Adds an item (unordered). Expands internal array if needed.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
public void Add(
T item
)
{
if (Count == _items.Length) {
EnsureCapacity(Count + 1);
@@ -144,7 +150,9 @@ namespace StgSharp.Collections
/// <summary>
/// Removes the first occurrence of value (unordered). Does nothing if not found.
/// </summary>
public void Remove(T value)
public void Remove(
T value
)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++)
@@ -162,7 +170,9 @@ namespace StgSharp.Collections
/// last). Preserves O(1) removal at cost of ordering.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
public void RemoveAt(
int index
)
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
@@ -181,13 +191,15 @@ namespace StgSharp.Collections
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int min)
private void EnsureCapacity(
int min
)
{
if (_items.Length >= min) {
return;
}
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
int newCapacity = (_items.Length == 0) ? 4 : (_items.Length * 2);
if (newCapacity < min) {
newCapacity = min;
}
@@ -1,30 +1,27 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="MatrixOperatorPolicy.cs"
// file="MatrixOperatorPolicy"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -36,7 +33,7 @@ using System.Threading.Tasks;
namespace StgSharp.Mathematics.Numeric.Runtime
{
internal enum MatrixCachePolicy
internal enum MatrixCachePolicy : short
{
None = 0,
@@ -25,6 +25,7 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Collections;
using StgSharp.HighPerformance.Memory;
using System;
using System.Collections.Generic;
@@ -41,7 +42,8 @@ namespace StgSharp.Mathematics.Numeric
private static NumaMeasurement[] _numaMeasurements;
private static int[] _numaPayload;
private static MatrixParallelThread[][] _numaThreads;
private static LinearBag<MatrixParallelThread>[] _numaSyncHeavy;
private static LinearBag<MatrixParallelThread>[] _numaWaveFront;
private static MatrixParallelThread[] _threads;
private static void UpdateThreadPayload()
@@ -65,12 +65,12 @@ namespace StgSharp.Mathematics.Numeric
ulong additionalPayload = 0;
for (int i = 0; i < _numaMeasurements.Length; i++)
{
MatrixParallelThread[] numa = _numaThreads[i];
MatrixParallelThread[] numa = _numaWaveFront[i];
if ((numa is null) || (numa.Length == 0))
{
continue;
}
ulong total = (ulong)_numaThreads[i].Length;
ulong total = (ulong)_numaWaveFront[i].Length;
NumaMeasurement measure = _numaMeasurements[i];
ulong numaPayload = measure.ActiveTasks;
uint numaActive = measure.ActiveThreadCount;
@@ -82,7 +82,7 @@ namespace StgSharp.Mathematics.Numeric
}
}
L4 cache = _numaBuffer[payloadIndex];
MatrixParallelThread[] selectedNuma = _numaThreads[payloadIndex];
MatrixParallelThread[] selectedNuma = _numaWaveFront[payloadIndex];
throw new NotImplementedException();
}
@@ -84,7 +84,7 @@ namespace StgSharp.Mathematics.Numeric
_retiredThreads = new(RESERVED_THREAD_COUNT);
// init thread on all logical cores
_numaThreads = Numa.CreateForeachThread<MatrixParallelThread>(
MatrixParallelThread[][] numaWaveOrigin = Numa.CreateForeachThread<MatrixParallelThread>(
core => {
MatrixParallelThread thread = new(core);
SpinWait.SpinUntil(() => thread.State == MatrixThreadState.Ready);
@@ -108,15 +108,15 @@ namespace StgSharp.Mathematics.Numeric
// retire threads
if (ContextCount == 1)
{
MatrixParallelThread[] threadArr = _numaThreads[0];
MatrixParallelThread[] threadArr = numaWaveOrigin[0];
int retireCount = Math.Min(RESERVED_THREAD_COUNT, Math.Max(0, threadArr.Length));
for (int i = 1; i <= retireCount; i++) {
RetireThread(threadArr[^i]);
}
_numaThreads[0] = threadArr[0..^retireCount];
_numaWaveFront[0] = [.. threadArr[0..^retireCount]];
} else
{
RetireWhenMultipleSIMDID();
RetireWhenMultipleSIMDID(numaWaveOrigin);
}
_meaningful = _threads.Length > 1;
@@ -134,11 +134,13 @@ namespace StgSharp.Mathematics.Numeric
_retiredThreads.Push(thread);
}
private static void RetireWhenMultipleSIMDID()
private static void RetireWhenMultipleSIMDID(
MatrixParallelThread[][] threads
)
{
MatrixParallelThread[] allThreads = new MatrixParallelThread[Environment.ProcessorCount];
int offset = 0;
foreach (MatrixParallelThread[] threadArr in _numaThreads)
foreach (MatrixParallelThread[] threadArr in threads)
{
Array.Copy(threadArr, 0, allThreads, offset, threadArr.Length);
offset += threadArr.Length;
@@ -159,9 +161,9 @@ namespace StgSharp.Mathematics.Numeric
}
// rebuild numa active arrays
for (int numa = 0; numa < _numaThreads.Length; numa++)
for (int numa = 0; numa < _numaWaveFront.Length; numa++)
{
MatrixParallelThread[] source = _numaThreads[numa];
MatrixParallelThread[] source = threads[numa];
int keepCount = 0;
for (int i = 0; i < source.Length; i++)
@@ -181,7 +183,7 @@ namespace StgSharp.Mathematics.Numeric
}
}
_numaThreads[numa] = active;
_numaWaveFront[numa] = [.. active];
}
_threads = allThreads[retireCount..];
+27 -22
View File
@@ -17,18 +17,18 @@
#define GET_MK_PROC_STD(T_type, T_arch, T_feature, T_op) \
((UNI_MK_PROC)MK_PROC_NAME_STD(T_type, T_arch, T_feature, T_op))
#define SN_MK_PROC_DECL(T_type, P_name) INTERNAL void SN_DECL P_name(MAT_TASK(T_type) const *task)
#define SN_MK_PROC_DECL_STD(T_type, T_arch, T_feature, T_op) \
INTERNAL NOINLINE SN_STD_TARGET_ATTR(T_arch, T_feature) void SN_DECL \
MK_PROC_NAME_STD(T_type, T_arch, T_feature, T_op)(MAT_TASK(T_type) const *task)
#define SN_MK_PROC_DECL_STD(T_type, T_arch, T_feature, T_op) \
INTERNAL NOINLINE SN_STD_TARGET_ATTR(T_arch, T_feature) void SN_DECL MK_PROC_NAME_STD( \
T_type, T_arch, T_feature, T_op)(MAT_TASK(T_type) const *task)
#define SK_PROC_NAME_STD(T_type, T_arch, T_feature, T_op) \
T_type##_single_kernel_proc_##T_op##_##T_arch##_##T_feature
#define GET_SK_PROC_STD(T_type, T_arch, T_feature, T_op) \
((UNI_SK_PROC)SK_PROC_NAME_STD(T_type, T_arch, T_feature, T_op))
#define SN_SK_PROC_DECL_STD(T_type, T_arch, T_feature, T_op) \
INTERNAL NOINLINE SN_STD_TARGET_ATTR(T_arch, T_feature) void SN_DECL \
SK_PROC_NAME_STD(T_type, T_arch, T_feature, T_op)( \
MAT_KERNEL(T_type) const *left, MAT_KERNEL(T_type) const *right, \
MAT_KERNEL(T_type) *restrict ans, uint64_t scalar)
#define SN_SK_PROC_DECL_STD(T_type, T_arch, T_feature, T_op) \
INTERNAL NOINLINE SN_STD_TARGET_ATTR(T_arch, T_feature) void SN_DECL SK_PROC_NAME_STD( \
T_type, T_arch, T_feature, \
T_op)(MAT_KERNEL(T_type) const *left, MAT_KERNEL(T_type) const *right, \
MAT_KERNEL(T_type) *restrict ans, uint64_t scalar)
#define _SN_STD__PROBE_0 0
#define _SN_STD__PROBE_1 1
@@ -43,27 +43,27 @@
#define _SN_MATPROC_RIGHT_ANS_SCALAR 1
#define _SN_MATPROC_ANS_SCALAR 1
#define _SN_MK_EXPAND_RIGHT_ANS \
#define _SN_MK_EXPAND_RIGHT_ANS \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2;
#define _SN_MK_EXPAND_LEFT_RIGHT_ANS \
#define _SN_MK_EXPAND_LEFT_RIGHT_ANS \
SN_MAT_KERNEL_LOCAL const *left = (SN_MAT_KERNEL_LOCAL const *)task->mat_0; \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2;
#define _SN_MK_EXPAND_RIGHT_ANS_SCALAR \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2; \
#define _SN_MK_EXPAND_RIGHT_ANS_SCALAR \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2; \
scalar_pack *restrict scalar = (scalar_pack *restrict)task->scalar;
#define _SN_MK_EXPAND_LEFT_RIGHT_ANS_SCALAR \
SN_MAT_KERNEL_LOCAL const *left = (SN_MAT_KERNEL_LOCAL const *)task->mat_0; \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2; \
#define _SN_MK_EXPAND_LEFT_RIGHT_ANS_SCALAR \
SN_MAT_KERNEL_LOCAL const *left = (SN_MAT_KERNEL_LOCAL const *)task->mat_0; \
SN_MAT_KERNEL_LOCAL const *right = (SN_MAT_KERNEL_LOCAL const *)task->mat_1; \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2; \
scalar_pack *restrict scalar = (scalar_pack *restrict)task->scalar;
#define _SN_MK_EXPAND_ANS_SCALAR \
#define _SN_MK_EXPAND_ANS_SCALAR \
SN_MAT_KERNEL_LOCAL *restrict ans = (SN_MAT_KERNEL_LOCAL *)task->mat_2; \
scalar_pack *restrict scalar = (scalar_pack *restrict)&(task->padding);
@@ -74,9 +74,14 @@
#pragma region mem layout
#define _SN_MATPROC_LAYOUT_BUFFER 1
#define _SN_MATPROC_LAYOUT_TILE 1
#define _SN_MATPROC_LAYOUT_KERNEL 1
#define _MK_MKPROC_EXPAND_INDEX_BUFFER \
size_t offset = UNSAFE_AS(size_t, task->padding.data[0]); \
size_t count = UNSAFE_AS(size_t, task->padding.data[1]);
#define _MK_MKPROC_EXPAND_INDEX_BUFFER \
size_t offset = UNSAFE_AS(size_t, task->padding.data[0]); \
size_t count = UNSAFE_AS(size_t, task->padding.data[1]);
@@ -99,13 +104,13 @@
#pragma endregion
#define __mk_param_std(mem, param, T) \
typedef MAT_KERNEL(T) SN_MAT_KERNEL_LOCAL; \
#define __mk_param_std(mem, param, T) \
typedef MAT_KERNEL(T) SN_MAT_KERNEL_LOCAL; \
matrix_task const *_ = (_SN_PARAM_STYLE(param), (_SN_MEM_LAYOUT(mem)), task); \
_SN_MK_EXPAND_PARAM(param); \
_SN_MK_EXPAND_PARAM(param); \
_SN_MK_EXPAND_MEM(mem)
#define __sk_param_std(T) \
#define __sk_param_std(T) \
typedef MAT_KERNEL(T) SN_MAT_KERNEL_LOCAL; \
(void)scalar
#endif // !SN_INTRINSIC_PARAM_EXPAND
+38 -35
View File
@@ -1,37 +1,34 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/28.2.5 Chrome/138.0.7204.251 Electron/37.6.1 Safari/537.36" version="28.2.5">
<diagram name="第 1 页" id="cbTJhW8NyRPDnIMFj9ZQ">
<mxGraphModel dx="2367" dy="1027" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="#797979" math="0" shadow="0">
<mxGraphModel dx="1516" dy="401" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="#797979" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="25TvGm5YAioSoPTIv9tO-4" value="视图托管接口VH&lt;br&gt;ViewHost" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=bottom;gradientColor=none;fillColor=#3399FF;fontColor=#000000;strokeColor=none;" parent="1" vertex="1">
<mxGeometry x="-210" y="40" width="430" height="200" as="geometry" />
<mxGeometry x="-116" y="40" width="250" height="200" as="geometry" />
</mxCell>
<mxCell id="oCZHK8Ia19RPNdtHcHY8-36" value="状态宿主 StateHost" style="verticalLabelPosition=middle;verticalAlign=top;html=1;shape=mxgraph.basic.rect;fillColor2=none;strokeWidth=1;size=0;indent=0;fillColor=#3399FF;labelPosition=center;align=center;strokeColor=none;" parent="1" vertex="1">
<mxGeometry x="-420" y="410" width="440" height="340" as="geometry" />
</mxCell>
<mxCell id="oCZHK8Ia19RPNdtHcHY8-37" value="呈现器VP&lt;br&gt;View Presenter" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-200" y="50" width="210" height="70" as="geometry" />
</mxCell>
<mxCell id="oCZHK8Ia19RPNdtHcHY8-38" value="视图描述VD&lt;br&gt;View Descriptor" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-200" y="120" width="210" height="70" as="geometry" />
<mxGeometry x="-106" y="50" width="110" height="140" as="geometry" />
</mxCell>
<mxCell id="oCZHK8Ia19RPNdtHcHY8-39" value="用户输入 I&lt;br&gt;Input" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="20" y="50" width="190" height="140" as="geometry" />
<mxGeometry x="15" y="50" width="99" height="140" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeColor=#66FF66;" parent="1" source="25TvGm5YAioSoPTIv9tO-4" target="25TvGm5YAioSoPTIv9tO-10" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="150" y="275" />
<mxPoint x="60" y="275" />
<mxPoint x="-7" y="275" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeColor=#66FF66;" parent="1" source="25TvGm5YAioSoPTIv9tO-4" target="25TvGm5YAioSoPTIv9tO-11" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="150" y="275" />
<mxPoint x="227" y="275" />
<mxPoint x="60" y="275" />
<mxPoint x="153" y="275" />
</Array>
</mxGeometry>
</mxCell>
@@ -54,11 +51,12 @@
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-57" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeColor=#FFFF33;" parent="1" source="25TvGm5YAioSoPTIv9tO-7" target="25TvGm5YAioSoPTIv9tO-53" edge="1">
<mxCell id="25TvGm5YAioSoPTIv9tO-57" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.336;entryY=1.014;entryDx=0;entryDy=0;strokeColor=#FFFF33;entryPerimeter=0;" parent="1" source="25TvGm5YAioSoPTIv9tO-7" target="25TvGm5YAioSoPTIv9tO-53" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-310" y="390" />
<mxPoint x="-310" y="390" />
<mxPoint x="-310" y="280" />
<mxPoint x="-280" y="280" />
<mxPoint x="-280" y="240" />
</Array>
</mxGeometry>
</mxCell>
@@ -69,7 +67,7 @@
<mxGeometry relative="1" as="geometry">
<mxPoint x="-40" y="440" as="targetPoint" />
<Array as="points">
<mxPoint x="60" y="390" />
<mxPoint x="-10" y="390" />
<mxPoint x="-40" y="390" />
<mxPoint x="-40" y="440" />
<mxPoint x="-39" y="440" />
@@ -77,30 +75,30 @@
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-10" value="StateChangeCommand" style="rounded=1;whiteSpace=wrap;html=1;fontColor=#000000;strokeColor=none;fillColor=#66FF66;" parent="1" vertex="1">
<mxGeometry x="-13" y="310" width="146" height="20" as="geometry" />
<mxGeometry x="-80" y="335" width="146" height="20" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.945;entryY=0.011;entryDx=0;entryDy=0;strokeColor=#33FF33;entryPerimeter=0;" parent="1" source="25TvGm5YAioSoPTIv9tO-11" target="25TvGm5YAioSoPTIv9tO-7" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="230" y="390" />
<mxPoint x="150" y="390" />
<mxPoint x="-40" y="390" />
<mxPoint x="-40" y="440" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-11" value="CallOperationCommand" style="rounded=1;whiteSpace=wrap;html=1;strokeColor=none;fillColor=#66FF66;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="154" y="310" width="146" height="20" as="geometry" />
<mxGeometry x="80" y="335" width="146" height="20" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=1;entryDx=0;entryDy=0;strokeColor=#FFFF33;" parent="1" source="25TvGm5YAioSoPTIv9tO-16" target="25TvGm5YAioSoPTIv9tO-4" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-107" y="275" />
<mxPoint x="-102" y="275" />
<mxPoint x="-160" y="280" />
<mxPoint x="-53" y="280" />
<mxPoint x="-53" y="240" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-16" value="CommandResult" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FFFF33;fontColor=#000000;strokeColor=none;" parent="1" vertex="1">
<mxGeometry x="-180" y="310" width="146" height="20" as="geometry" />
<mxGeometry x="-236" y="335" width="146" height="20" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-19" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;前端命令 FrontCommand&lt;/h1&gt;&lt;p&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;View和State通过前端命令进行交互&lt;br&gt;进程内通信可以直接传递对象&lt;br&gt;跨进程交互可以传递序列化后的json数据包&lt;br&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;State中响应command的结果通过CommandResult&lt;br&gt;数据包返回给View&lt;/p&gt;&lt;p&gt;在需要时,View通过前端命令获取State的快照进行图形化展示&lt;/p&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;align=left;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="-790" y="250" width="290" height="190" as="geometry" />
@@ -216,34 +214,36 @@
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-58" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;strokeColor=#66FF66;" parent="1" source="25TvGm5YAioSoPTIv9tO-53" target="25TvGm5YAioSoPTIv9tO-7" edge="1">
<mxCell id="25TvGm5YAioSoPTIv9tO-58" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;strokeColor=#66FF66;exitX=0.511;exitY=0.992;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="25TvGm5YAioSoPTIv9tO-53" target="25TvGm5YAioSoPTIv9tO-7" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-290" y="290" />
<mxPoint x="-290" y="290" />
<mxPoint x="-260" y="240" />
<mxPoint x="-260" y="300" />
<mxPoint x="-290" y="300" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-53" value="ViewHost 1" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=middle;gradientColor=none;fillColor=#3399FF;fontColor=#000000;strokeColor=none;" parent="1" vertex="1">
<mxGeometry x="-400" y="210" width="180" height="30" as="geometry" />
<mxGeometry x="-340" y="210" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-54" value="ViewHost 2" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=middle;gradientColor=none;fillColor=#3399FF;fontColor=#000000;strokeColor=none;" parent="1" vertex="1">
<mxGeometry x="-577" y="40" width="167" height="200" as="geometry" />
<mxGeometry x="-527" y="40" width="167" height="200" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-55" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#FFFF33;exitX=0;exitY=0;exitDx=0;exitDy=0;" parent="1" source="25TvGm5YAioSoPTIv9tO-7" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-355.34000000000003" y="390" as="sourcePoint" />
<mxPoint x="-490" y="240" as="targetPoint" />
<mxPoint x="-450" y="240" as="targetPoint" />
<Array as="points">
<mxPoint x="-380" y="330" />
<mxPoint x="-490" y="330" />
<mxPoint x="-450" y="330" />
<mxPoint x="-450" y="240" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-59" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.056;entryY=0.015;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#66FF66;" parent="1" source="25TvGm5YAioSoPTIv9tO-54" target="25TvGm5YAioSoPTIv9tO-7" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="310" />
<mxPoint x="-410" y="310" />
<mxPoint x="-360" y="310" />
</Array>
</mxGeometry>
@@ -298,13 +298,16 @@
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-78" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="-420" y="870" width="120" height="180" as="geometry" />
<mxGeometry x="-420" y="870" width="210" height="180" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-79" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#0066CC;fontColor=#ffffff;strokeColor=#F5F5F5;" parent="25TvGm5YAioSoPTIv9tO-78" vertex="1">
<mxCell id="25TvGm5YAioSoPTIv9tO-79" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#0066CC;fontColor=#ffffff;strokeColor=#F5F5F5;verticalAlign=top;" parent="25TvGm5YAioSoPTIv9tO-78" vertex="1">
<mxGeometry width="119.99999999999999" height="180" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-80" value="服务 1&lt;br&gt;Service 1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="25TvGm5YAioSoPTIv9tO-78" vertex="1">
<mxGeometry y="75" width="119.99999999999999" height="30" as="geometry" />
<mxGeometry y="10" width="119.99999999999999" height="30" as="geometry" />
</mxCell>
<mxCell id="iwdWNZ0WjOj6HhX6wyoA-1" value="呈现器VP&lt;br&gt;View Presenter" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="25TvGm5YAioSoPTIv9tO-78">
<mxGeometry y="50" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-82" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="-79" y="870" width="120" height="40" as="geometry" />
@@ -335,12 +338,12 @@
<mxGeometry x="-780" y="740" width="330" height="155" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-90" value="跨进程界面&lt;br&gt;ExternalDisplaySurface" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#FFFF99;fillColor=#FFFF99;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="-400" y="40" width="180" height="130" as="geometry" />
<mxGeometry x="-340" y="40" width="180" height="130" as="geometry" />
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-92" value="" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endWidth=9.056603773584905;endSize=2.0638888888888887;startWidth=9.865229110512129;startSize=1.2074074074074073;width=2.7777777777777777;" parent="1" source="25TvGm5YAioSoPTIv9tO-53" target="25TvGm5YAioSoPTIv9tO-90" edge="1">
<mxGeometry width="100" height="100" relative="1" as="geometry">
<mxPoint x="-360" y="290" as="sourcePoint" />
<mxPoint x="-260" y="190" as="targetPoint" />
<mxPoint x="-300" y="290" as="sourcePoint" />
<mxPoint x="-200" y="190" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="25TvGm5YAioSoPTIv9tO-93" value="json数据包" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;fontColor=#000000;" parent="25TvGm5YAioSoPTIv9tO-92" vertex="1" connectable="0">