Merge pull request 'perf: 给基础体构造加计时,定位到 DrawPipe/DrawCone/DrawLoft 共占 33%' (#109) from company-desktop/company-desktop-computer/perf-primitive-counters into main

Reviewed-on: #109
This commit was merged in pull request #109.
This commit is contained in:
gjm committed 2026-10-29 16:00:21 +08:00
commit 17e77b448d
3 files changed
+219

No files matched your search

@@ -1,5 +1,6 @@
#include "StdAfx.h"
#include "GiDrawutility.h"
#include "PerfCounterLite.h"
#include "EarThwormDef.h"
namespace
@@ -262,6 +263,7 @@ AECDLLIMPEXP AcDbEntity* GiDrawUtility::DrawLoft(const AcDbRegion *pTop
, const AcDbRegion *pBottom
, const AcDbCurve *pLine)
{
perf_lite::Scope perfScope("GDU::DrawLoft(reg)");
AcDbRegion *_pTop = (AcDbRegion*)pTop->clone();
AcDbRegion *_pBottom = (AcDbRegion*)pBottom->clone();
@@ -297,6 +299,7 @@ AECDLLIMPEXP AcDbEntity* GiDrawUtility::DrawLoft(const AcDbCurve *pTop
, const AcDbCurve *pBottom
, const AcDbCurve *pLine)
{
perf_lite::Scope perfScope("GDU::DrawLoft(curve)");
if (!pTop || !pBottom || !pLine) return NULL;
AcDbCurve *_pTop = (AcDbCurve*)pTop->clone();
@@ -385,6 +388,7 @@ AECDLLIMPEXP AcDbEntity* GiDrawUtility::DrawCone(const AcGePoint3d &ptPos
, const double &H
, const double &xOffset)
{
perf_lite::Scope perfScope("GDU::DrawCone");
AcGePoint3d ptBottom(AcGePoint3d::kOrigin);
AcGePoint3d ptTop(ptBottom + xOffset * AcGeVector3d::kXAxis);
ptTop.z = H;
@@ -684,6 +688,7 @@ AECDLLIMPEXP AcDbEntity* GiDrawUtility::DrawCylinder(const AcGePoint3d &ptFr
, const AcGePoint3d &ptTo
, const double &dRadius)
{
perf_lite::Scope perfScope("GDU::DrawCylinder");
return DrawPipe(ptFr, ptTo, dRadius, 0.);
}
@@ -782,6 +787,7 @@ AcDbEntity* GiDrawUtility::DrawPipe(const AcGePoint3d& ptFrom
, const double &dThick
, const int &nSegs /*= 20*/)
{
perf_lite::Scope perfScope("GDU::DrawPipe(pts)");
// 零长度路径无法扫掠
if (EQUAL_ZERO(ptFrom.distanceTo(ptTo))) return NULL;
@@ -844,6 +850,7 @@ AcDbEntity* GiDrawUtility::DrawPipe(const AcDbCurve* pCurve
AcDbEntity* GiDrawUtility::DrawPipe(const AcDbCurve *pCurve, double dR)
{
perf_lite::Scope perfScope("GDU::DrawPipe(curve,dR)");
if (!pCurve) return NULL;
if (DOWNEQUAL_ZERO(dR)) return NULL;
@@ -940,6 +947,7 @@ AcDbEntity *GiDrawUtility::DrawPipe(
, const AcDbRegion *pRegion
, const AcGePoint3d &ptCenInRgn)
{
perf_lite::Scope perfScope("GDU::DrawPipe(region)");
if (!pRegion || !pCurve) return NULL;
AcGePoint3d ptStart(AcGePoint3d::kOrigin);
+147
View File
@@ -0,0 +1,147 @@
// PerfCounterLite.h —— 给 AecRxBase 这类"不链接 CoreLibrary"的模块用的轻量计数器
//
// 为什么不复用 SectionPerf:CoreLibrary 是静态库且只被 CoreCommand 链接,
// AecRxBase.arx 里拿不到它的符号;所以这里做成 header-only。
//
// 行为:
// - 仅在 Debug(NDEBUG 未定义)且 AEC_PERF_COUNTER=1 时开启,Release 零开销
// - 每个 tag 统计:调用次数、累计耗时、平均/最长
// - 每 200 次调用重写一次 %TEMP%\aec_prim_perf.txt(快照)
// —— 故意不写 aec_section_perf.txt:那个文件是自动化脚本判断"开图完成"的信号,
// 提前写会让脚本误判并提前关闭 CAD
#ifndef PERF_COUNTER_LITE_H
#define PERF_COUNTER_LITE_H
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <string>
namespace perf_lite
{
struct Entry
{
Entry() : n(0), us(0), maxus(0) {}
long long n;
long long us;
long long maxus;
};
inline bool Enabled()
{
#ifndef NDEBUG
static int s_nOn = -1;
if (s_nOn < 0)
{
const char* psz = ::getenv("AEC_PERF_COUNTER");
s_nOn = (psz != NULL && psz[0] != '\0' && psz[0] != '0') ? 1 : 0;
}
return s_nOn == 1;
#else
return false;
#endif
}
inline std::map<std::string, Entry>& Stats()
{
static std::map<std::string, Entry> s_map;
return s_map;
}
inline long long QpcNow()
{
LARGE_INTEGER li;
::QueryPerformanceCounter(&li);
return (long long)li.QuadPart;
}
inline long long QpcFreq()
{
static long long s_nFreq = 0;
if (s_nFreq == 0)
{
LARGE_INTEGER li;
if (!::QueryPerformanceFrequency(&li) || li.QuadPart == 0)
s_nFreq = 1;
else
s_nFreq = (long long)li.QuadPart;
}
return s_nFreq;
}
inline void Dump()
{
char szTmp[MAX_PATH + 1];
::memset(szTmp, 0, sizeof(szTmp));
if (::GetTempPathA(MAX_PATH, szTmp) == 0)
return;
std::string sPath(szTmp);
sPath += "aec_prim_perf.txt";
FILE* fp = NULL;
if (fopen_s(&fp, sPath.c_str(), "w") != 0 || fp == NULL)
return;
fprintf(fp, "---- primitive builders (per tag) ----\n");
fprintf(fp, " count total_ms avg_us max_us tag\n");
const std::map<std::string, Entry>& m = Stats();
for (std::map<std::string, Entry>::const_iterator it = m.begin(); it != m.end(); ++it)
{
const Entry& e = it->second;
const double dAvg = e.n ? ((double)e.us / (double)e.n) : 0.0;
fprintf(fp, " %-9lld %10.1f %10.1f %10lld %s\n",
e.n, (double)e.us / 1000.0, dAvg, e.maxus, it->first.c_str());
}
fclose(fp);
}
inline void Add(const char* pszTag, long long nStart)
{
const long long nUs = (QpcNow() - nStart) * 1000000LL / QpcFreq();
Entry& e = Stats()[std::string(pszTag != NULL ? pszTag : "?")];
++e.n;
e.us += nUs;
if (nUs > e.maxus)
e.maxus = nUs;
// 每 200 次落一次快照,开图结束后文件里就是全量统计
static long long s_nTotal = 0;
if (++s_nTotal % 200 == 0)
Dump();
}
// RAII:函数入口构造即可
class Scope
{
public:
explicit Scope(const char* pszTag) : m_pszTag(pszTag), m_nStart(0)
{
if (Enabled())
m_nStart = QpcNow();
}
~Scope()
{
if (m_nStart != 0)
Add(m_pszTag, m_nStart);
}
private:
Scope(const Scope&);
Scope& operator=(const Scope&);
const char* m_pszTag;
long long m_nStart;
};
// 在卸载时再补一次,确保最后的零头也落盘
inline void Flush()
{
if (Enabled())
Dump();
}
}
#endif // PERF_COUNTER_LITE_H