diff --git a/SourceCode/Code2026/CoreLibrary/AecDbX3dPart.cpp b/SourceCode/Code2026/CoreLibrary/AecDbX3dPart.cpp index fdf7375..30ab4ce 100644 --- a/SourceCode/Code2026/CoreLibrary/AecDbX3dPart.cpp +++ b/SourceCode/Code2026/CoreLibrary/AecDbX3dPart.cpp @@ -20,6 +20,138 @@ static const Adesk::UInt16 kMagenta = 6; static const Adesk::UInt16 kWhite = 7; static const Adesk::UInt16 kColorByLayer = 256; +// =========================================================================== +// 试点探针(AEC_X3D_SIZEPROBE=1 才生效,Release 零开销) +// +// 目的:量清楚"把一个 part 的 m_pSolid 存进 DWG"到底要多大、多贵。 +// - acisOut 出来的 SAT 文本字节数:二进制 SAB 一般比它小,所以这是体积**上界**; +// - acisOut 的耗时:二进制写只要格式化更少,所以这是"序列化一次"的耗时**上界**; +// - 顺带对比同一次 Rebuild 的耗时,算出"恢复 vs 重建"的比值。 +// 汇总写 %TEMP%\aec_x3d_probe.txt(每 100 个 part 刷新一次)。 +// =========================================================================== +namespace +{ + struct X3dProbeStat + { + X3dProbeStat() : n(0), bytes(0), maxBytes(0), outUs(0), rebuildUs(0) {} + long long n; + long long bytes; + long long maxBytes; + long long outUs; + long long rebuildUs; + }; + + X3dProbeStat& ProbeStat() { static X3dProbeStat s; return s; } + + bool ProbeEnabled() + { + static int s_nOn = -1; + if (s_nOn < 0) + { + const char* psz = ::getenv("AEC_X3D_SIZEPROBE"); + s_nOn = (psz != NULL && psz[0] != '\0' && psz[0] != '0') ? 1 : 0; + } + return s_nOn == 1; + } + + long long ProbeQpc() + { + LARGE_INTEGER li; + ::QueryPerformanceCounter(&li); + return (long long)li.QuadPart; + } + + long long ProbeQpcFreq() + { + static long long s_f = 0; + if (s_f == 0) + { + LARGE_INTEGER li; + if (!::QueryPerformanceFrequency(&li) || li.QuadPart == 0) s_f = 1; + else s_f = (long long)li.QuadPart; + } + return s_f; + } + + void ProbeDump() + { + TCHAR szTmp[MAX_PATH + 1]; + ::memset(szTmp, 0, sizeof(szTmp)); + if (::GetTempPath(MAX_PATH, szTmp) == 0) + return; + TCHAR szFile[MAX_PATH + 1]; + ::_stprintf_s(szFile, MAX_PATH, _T("%saec_x3d_probe.txt"), szTmp); + + FILE* fp = NULL; + if (_tfopen_s(&fp, szFile, _T("w")) != 0 || fp == NULL) + return; + + const X3dProbeStat& s = ProbeStat(); + fprintf(fp, "---- X3dPart geometry size probe ----\n"); + fprintf(fp, "parts measured : %lld\n", s.n); + fprintf(fp, "total SAT bytes : %lld (%.1f MB)\n", s.bytes, (double)s.bytes / 1048576.0); + if (s.n > 0) + { + fprintf(fp, "avg SAT bytes/part : %lld\n", s.bytes / s.n); + fprintf(fp, "max SAT bytes/part : %lld\n", s.maxBytes); + fprintf(fp, "avg acisOut per part: %.3f ms\n", (double)s.outUs / (double)s.n / 1000.0); + fprintf(fp, "avg Rebuild per part: %.3f ms\n", (double)s.rebuildUs / (double)s.n / 1000.0); + if (s.outUs > 0) + fprintf(fp, "rebuild / acisOut : %.1f x\n", (double)s.rebuildUs / (double)s.outUs); + } + fclose(fp); + } + + // 对一个 solid 量 SAT 字节数 + acisOut 耗时 + void ProbeOneSolid(AcDb3dSolid* pSolid, long long nRebuildUs) + { + if (!ProbeEnabled()) + return; + + X3dProbeStat& s = ProbeStat(); + ++s.n; + s.rebuildUs += nRebuildUs; + + if (pSolid != NULL) + { + TCHAR szTmp[MAX_PATH + 1]; + ::memset(szTmp, 0, sizeof(szTmp)); + if (::GetTempPath(MAX_PATH, szTmp) != 0) + { + TCHAR szFile[MAX_PATH + 1]; + ::_stprintf_s(szFile, MAX_PATH, _T("%saec_x3d_probe_%lu.sat"), + szTmp, (unsigned long)::GetCurrentProcessId()); + + AcDbVoidPtrArray arSolids; + arSolids.append(pSolid); + + const long long t0 = ProbeQpc(); + Acad::ErrorStatus es = AcDbBody::acisOut(szFile, arSolids); + const long long t1 = ProbeQpc(); + s.outUs += (t1 - t0) * 1000000LL / ProbeQpcFreq(); + + if (es == Acad::eOk) + { + WIN32_FILE_ATTRIBUTE_DATA fad; + ::memset(&fad, 0, sizeof(fad)); + if (::GetFileAttributesEx(szFile, GetFileExInfoStandard, &fad)) + { + const long long nSize = + ((long long)fad.nFileSizeHigh << 32) | (long long)fad.nFileSizeLow; + s.bytes += nSize; + if (nSize > s.maxBytes) + s.maxBytes = nSize; + } + ::DeleteFile(szFile); + } + } + } + + if (s.n % 100 == 0) + ProbeDump(); + } +} + static void FreeBuffer(int n,AcDbVoidPtrArray& arEntPtrs) { AcDbEntity* pEnt = NULL; @@ -960,7 +1092,11 @@ bool AecDbX3dPart::EnsureCreated() const SectionPerf::NoteEnsure("X3dPart", this, true); //试点计时:X3dPart 重建总耗时(用来看"几何存盘"这套机制的天花板在哪) SectionPerf::Scope perfScope("X3dPart::Rebuild"); + const bool bProbe = ProbeEnabled(); + const long long nProbeT0 = bProbe ? ProbeQpc() : 0; const bool bOk = const_cast(this)->Rebuild(); + if (bProbe) + ProbeOneSolid(m_pSolid, (ProbeQpc() - nProbeT0) * 1000000LL / ProbeQpcFreq()); SectionPerf::NoteEnsure(m_pSolid != NULL ? "X3dPart.rebuild_ok" : "X3dPart.rebuild_null", this, false); if (m_pSolid == NULL) {