perf: 护栏/爬梯等构件的管道副本改为"只存参数",消除开图期 1140 次截面回写 #104

Merged
gjm merged 1 commits from company-desktop/company-desktop-computer/perf-defer-pipe-clone AGit into main 2026-10-28 22:01:41 +08:00
10 changed files with 146 additions and 31 deletions

No files matched your search

@@ -1126,7 +1126,7 @@ AcDb3dSolid *AecDbBossHolder::Create3dRod(const AcGePoint3d &ptFr, const AcGePoi
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe *)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -1145,7 +1145,7 @@ AcDb3dSolid* AecDbBossHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoin
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -1157,7 +1157,7 @@ AcDb3dSolid* AecDbBossHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoin
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -1169,7 +1169,7 @@ AcDb3dSolid* AecDbBossHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoin
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -669,7 +669,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawLadderH3d(AcGiDraw &dc,const AcGeVector3d
pEnt3d = dc.DrawPipe(&ln,pReg,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(&ln);
pipe.SetSection(pReg,AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -692,7 +692,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawLadderH3d(AcGiDraw &dc,const AcGeVector3d
pEnt3d = dc.DrawPipe(&ln,pReg,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(&ln);
pipe.SetSection(pReg, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -705,7 +705,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawLadderH3d(AcGiDraw &dc,const AcGeVector3d
pEnt3d = dc.DrawPipe(&ln,pReg,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(&ln);
pipe.SetSection(pReg, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -732,7 +732,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawLadderH3d(AcGiDraw &dc,const AcGeVector3d
pEnt3d = dc.DrawPipe(&ln,pReg,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(&ln);
pipe.SetSection(pReg, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -752,7 +752,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawLadderH3d(AcGiDraw &dc,const AcGeVector3d
pEnt3d = dc.DrawPipe(&ln,pReg,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(&ln);
pipe.SetSection(pReg, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -848,7 +848,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawGuardBar3d(AcGiDraw &dc,const AcGeVector3
pEnt3d = dc.DrawPipe(pPl,pRegCln,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(pPl);
pipe.SetSection(pRegCln, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -868,7 +868,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawGuardBar3d(AcGiDraw &dc,const AcGeVector3
pEnt3d = dc.DrawPipe(pPl,pRegCln,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(pPl);
pipe.SetSection(pRegCln, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -899,7 +899,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawGuardBar3d(AcGiDraw &dc,const AcGeVector3
pEnt3d = dc.DrawPipe(pLn,pRegCln,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(pLn);
pipe.SetSection(pRegCln, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -924,7 +924,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawGuardBar3d(AcGiDraw &dc,const AcGeVector3
pEnt3d = dc.DrawPipe(pLn,pRegCln,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(pLn);
pipe.SetSection(pRegCln, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -955,7 +955,7 @@ Acad::ErrorStatus AecDbClimbStair::DrawGuardBar3d(AcGiDraw &dc,const AcGeVector3
pEnt3d = dc.DrawPipe(pLn,pRegCln,AcGePoint3d::kOrigin);
pipe.SetBaseCurve(pLn);
pipe.SetSection(pRegCln, AcGePoint3d::kOrigin);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
if (pEnt3d != NULL)
{
Add3DBuffer(pEnt3d);
@@ -7820,6 +7820,59 @@ AcDbObjectId AecDbEwPipeBase::GetCenterLayer() const
return m_idCenterLayer;
}
AcDbRegion* AecDbEwPipeBase::BuildSectionFromXData() const
{
int nShape = 0;
if (!m_mapSpecXData.GetAt(KEY_SECSHAPE, nShape))
return NULL;
double d1 = 0.0, d2 = 0.0, dt = 0.0;
if (!m_mapSpecXData.GetAt(KEY_SIZE1, d1))
return NULL;
m_mapSpecXData.GetAt(KEY_SIZE2, d2);
m_mapSpecXData.GetAt(PIPE_THICK, dt);
// 借一个临时对象复用 SetSection 的全部逻辑,取到 clone 后临时对象析构
AecDbEwPipeBase tmp;
Acad::ErrorStatus es = Acad::eInvalidInput;
if (nShape == eCircle)
es = tmp.SetSection(d1, dt);
else if (nShape == eRect)
es = tmp.SetSection(d1, d2, dt);
else
return NULL; // 自定义截面(SAT 里才有)无法由参数重建
if (es != Acad::eOk)
return NULL;
return tmp.GetSection();
}
AecDbEwPipeBase* AecDbEwPipeBase::CloneParamsOnly() const
{
AecDbEwPipeBase* pNew = (AecDbEwPipeBase*)AecDbEwPipeBase::desc()->create();
if (pNew == NULL)
return NULL;
// 图层/颜色/线型等实体属性
pNew->setPropertiesFrom(this);
// 专业数据(含截面参数、GUID 等)
pNew->m_mapSpecXData.CopyFrom(&m_mapSpecXData);
pNew->m_sGUID = m_sGUID;
// 放样线
AcDbCurve* pCurve = GetBaseCurve();
if (pCurve != NULL)
{
pNew->SetBaseCurve(pCurve);
DELETE_PTR(pCurve);
}
pNew->m_nInfActive = m_nInfActive;
pNew->m_arBreaks.removeAll();
pNew->m_arBreaks.append(m_arBreaks);
// 故意不复制 m_pRegion:截面交给 ToJson() 按参数重建
return pNew;
}
SHJson::WValue& AecDbEwPipeBase::ToJson(bool bExprotGeo) const
{
using namespace SHJson;
@@ -7836,6 +7889,8 @@ SHJson::WValue& AecDbEwPipeBase::ToJson(bool bExprotGeo) const
addMember(v, _T("__path__"), GEO2JSON::path(*pBaseCurve).ToJson());
AcDbRegion *pReg(GetSection());
if (pReg == NULL)
pReg = BuildSectionFromXData(); // 延迟导出:绘制期没留几何,这里按参数重建
if (pReg)
{
addMember(v, _T("__section__"), GEO2JSON::region(*pReg).ToJson());
@@ -1003,7 +1003,7 @@ AcDb3dSolid* AecDbHanger::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSection(dL1,0.0);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -1015,7 +1015,7 @@ AcDb3dSolid* AecDbHanger::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -1027,7 +1027,7 @@ AcDb3dSolid* AecDbHanger::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
}
@@ -870,7 +870,7 @@ AcDb3dSolid* AecDbHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSection(dL1,0.0);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -882,7 +882,7 @@ AcDb3dSolid* AecDbHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
@@ -894,7 +894,7 @@ AcDb3dSolid* AecDbHolder::Create3dRod(const AcGePoint3d& ptFr,const AcGePoint3d&
pipe.SetSectionRotation(dRegRoate);
pipe.SetStartPoint(ptFr);
pipe.SetEndPoint(ptTo);
m_pipes.push_back((AecDbEwPipe*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Draw3d(dcTmp);
pPipe = pipe.GetCreatPipe3dSolid();
}
@@ -916,7 +916,7 @@ Acad::ErrorStatus AecDbMRPLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray
if (data.nGarBarLR != 1)//左有
{
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
}
@@ -932,7 +932,7 @@ Acad::ErrorStatus AecDbMRPLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray
if (data.nGarBarLR > 0)//右有
{
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
}
}
@@ -967,7 +967,7 @@ Acad::ErrorStatus AecDbMRPLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray
if (data.nGarBarLR != 1)//左有
{
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
}
delete pPl;
@@ -986,7 +986,7 @@ Acad::ErrorStatus AecDbMRPLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray
if (data.nGarBarLR > 0)//右有
{
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
}
@@ -1073,7 +1073,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
@@ -1086,7 +1086,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
}
@@ -1102,7 +1102,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
//扶手伸出
@@ -1116,7 +1116,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
//对称位置
@@ -1127,7 +1127,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
//扶手伸出
@@ -1137,7 +1137,7 @@ Acad::ErrorStatus AecDbZLineStair::DrawGuarBar3d(AcGiDraw &dc,AcGePoint3dArray&
pipe.SetBaseCurve(&ln);
pipe.Draw3d(dc2);
m_pipes.push_back((AecDbEwPipeBase*)pipe.clone());
m_pipes.push_back(pipe.CloneParamsOnly()); // 只存参数,截面在 ToJson() 时重建
pipe.Clean3DBufferNoDel();
int nCrbk = dc.GetColor();
@@ -411,7 +411,22 @@ namespace SectionPerf
sprintf_s(szLine, sizeof(szLine), " %-28s +0x%llx\n", szMod, (unsigned long long)(nAddr - (DWORD64)hMod));
out += szLine;
}
AppendReport(out.c_str());
// 写到独立文件:不打扰 ENVIPERFDUMP 的报告文件(脚本靠它判断就绪)
{
char szTmp[MAX_PATH + 1];
::memset(szTmp, 0, sizeof(szTmp));
if (::GetTempPathA(MAX_PATH, szTmp) != 0)
{
std::string sPath(szTmp);
sPath += "aec_writeback_trace.txt";
FILE* fp = NULL;
if (fopen_s(&fp, sPath.c_str(), "ab") == 0 && fp != NULL)
{
fprintf(fp, "%s", out.c_str());
fclose(fp);
}
}
}
}
void AppendReport(const char* pszText)
@@ -239,6 +239,14 @@ public:
//获取截面的复制
AcDbRegion* GetSection() const;
// 按 XData 里存的截面参数(SECS HAP/SIZE1/SIZE2/THICK)重建截面,调用方负责释放。
// 用于"延迟导出":绘制期只留参数副本,导出 JSON 时再建几何。
AcDbRegion* BuildSectionFromXData() const;
// 生成一个只带参数(不含截面几何)的副本,供 m_pipes 之类"延迟导出"的容器使用。
// 与 clone() 的区别:不触发 dwgOutFields 序列化,因此不会把截面写成临时 SAT 文件。
AecDbEwPipeBase* CloneParamsOnly() const;
//设置界面绕轴旋转,以X 方向为基准
Acad::ErrorStatus SetSectionRotation(double dAngle);
+37
View File
@@ -377,6 +377,43 @@ pClone->dwgIn(&filer); // → dwgInFields → acisIn 读回来
> 再用 `tools/resolve_rva.ps1` 把"模块+RVA"解析成函数名。
> 单靠栈不解析符号很容易误判(本次就误判了两次)。
### 开图期回写的修复:把 clone 延迟到 ToJson()
按上面的结论,`m_pipes` 的唯一用途是 `ToJson()` 导出 `__pipes__`,
而截面参数(`KEY_SECSHAPE` / `KEY_SIZE1` / `KEY_SIZE2` / `PIPE_THICK` / `KEY_SECCEN`)
在建截面时已经写进 spec XData,**完全可以按参数重建**。
所以绘制期不必保留几何,等导出时再建。
改动:
1. `AecDbEwPipeBase` 新增两个方法
- `BuildSectionFromXData()`:按 XData 参数重建截面(圆形 / 矩形),自定义截面返回 NULL
- `CloneParamsOnly()`:生成只带参数(XData / 放样线 / 实体属性 / GUID / Breaks)、
**不含截面几何**的副本
2. `AecDbEwPipeBase::ToJson()`:`GetSection()` 为空时回退到 `BuildSectionFromXData()`,
保证 `__section__` / `__section2__` 仍在
3. 把所有 `m_pipes.push_back((AecDbEwPipeBase*)pipe.clone())` 改成
`m_pipes.push_back(pipe.CloneParamsOnly())`,共 **30 处 / 6 个文件**:
ClimbStair(10)、ZLineStair(6)、BossHolder(4)、MRPLineStair(4)、Hanger(3)、Holder(3)
先只改楼梯两处时,回写从 1140 降到 960 —— 用 `AEC_WRITEBACK_TRACE` 再抓栈,
解析出剩余 960 全部来自 `AecDbClimbStair::DrawLadderH3d`(爬梯),
恰好对应 ClimbStair 的 10 处,于是把其余 20 处一并改掉。
实测(big1.dwg,AutoCAD 2020 / R230 Debug):
| 指标 | 初始 | 加缓存后 | 本次修复后 |
| --- | ---: | ---: | ---: |
| 开图墙钟 | 108~113 s | 98 s | **93 s** |
| 开图期回写 | 1140 次 / 2.38 s | 1140 次 | **0** |
| `EwPipe::RegionAcisIn` | 3082 次 | 3082 次 | **1942 次** |
| 临时文件 | 6773 次 / 9.34 MB | 2694 次 / 3.34 MB | **1733 次 / 2.34 MB** |
| 截面恢复总耗时 | 22.4 s | 9.87 s | **9.4 s** |
**待验证(本次没条件跑)**:JSON 导出。`m_pipes` 里现在是"无几何的参数副本",
`ToJson()` 靠 `BuildSectionFromXData()` 重建,需要实际触发一次导出,
确认 `__pipes__` 里每条仍有 `__section__` / `__section2__` 且数值与改动前一致。
### Phase 2:临时目录治理(可选,止血)
Phase 1 之后残余落盘只剩"每个唯一截面一次",收益小;但如果现场仍被杀软拖慢,可做这一步: