【发布时间】:2010-09-23 12:07:22
【问题描述】:
这可能吗?
【问题讨论】:
-
我从未见过在任何地方测量过这个参数。
这可能吗?
【问题讨论】:
并非如此,但您可以使用供应商的实用程序获得不同的性能计数器,对于 NVIDIA,您有 NVPerfKit 和 NVPerfHUD。其他供应商也有类似的实用程序。
【讨论】:
不。在这样一个高度并行的环境中,甚至很难严格定义。但是,您可以使用 ARB_timer_query 扩展来近似它。
【讨论】:
我在我的 OpenGL 渲染线程实现中实现了一个基于计时器查询的 GPU 执行时间测量框架。我将在下面分享计时器查询部分:
假设
enqueue 在渲染线程上运行一个函数limiter.frame60 每 60 帧只等于 0 一次代码:
struct TimerQuery
{
std::string description;
GLuint timer;
};
typedef std::deque<TimerQuery> TimerQueryQueue;
...
TimerQueryQueue timerQueryQueue;
...
void GlfwThread::beginTimerQuery(std::string description)
{
if (limiter.frame60 != 0)
return;
enqueue([this](std::string const& description) {
GLuint id;
glGenQueries(1, &id);
timerQueryQueue.push_back({ description, id });
glBeginQuery(GL_TIME_ELAPSED, id);
}, std::move(description));
}
void GlfwThread::endTimerQuery()
{
if (limiter.frame60 != 0)
return;
enqueue([this]{
glEndQuery(GL_TIME_ELAPSED);
});
}
void GlfwThread::dumpTimerQueries()
{
while (!timerQueryQueue.empty())
{
TimerQuery& next = timerQueryQueue.front();
int isAvailable = GL_FALSE;
glGetQueryObjectiv(next.timer,
GL_QUERY_RESULT_AVAILABLE,
&isAvailable);
if (!isAvailable)
return;
GLuint64 ns;
glGetQueryObjectui64v(next.timer, GL_QUERY_RESULT, &ns);
DebugMessage("timer: ",
next.description, " ",
std::fixed,
std::setprecision(3), std::setw(8),
ns / 1000.0, Stopwatch::microsecText);
glDeleteQueries(1, &next.timer);
timerQueryQueue.pop_front();
}
}
这是一些示例输出:
Framerate t=5.14 fps=59.94 fps_err=-0.00 aet=2850.67μs adt=13832.33μs alt=0.00μs cpu_usage=17%
instanceCount=20301 parallel_μs=2809
timer: text upload range 0.000μs
timer: clear and bind 95.200μs
timer: upload 1.056μs
timer: draw setup 1.056μs
timer: draw 281.568μs
timer: draw cleanup 1.024μs
timer: renderGlyphs 1.056μs
Framerate t=6.14 fps=59.94 fps_err=0.00 aet=2984.55μs adt=13698.45μs alt=0.00μs cpu_usage=17%
instanceCount=20361 parallel_μs=2731
timer: text upload range 0.000μs
timer: clear and bind 95.232μs
timer: upload 1.056μs
timer: draw setup 1.024μs
timer: draw 277.536μs
timer: draw cleanup 1.056μs
timer: renderGlyphs 1.024μs
Framerate t=7.14 fps=59.94 fps_err=-0.00 aet=3007.05μs adt=13675.95μs alt=0.00μs cpu_usage=18%
instanceCount=20421 parallel_μs=2800
timer: text upload range 0.000μs
timer: clear and bind 95.232μs
timer: upload 1.056μs
timer: draw setup 1.056μs
timer: draw 281.632μs
timer: draw cleanup 1.024μs
timer: renderGlyphs 1.056μs
这使我可以在我的 opengl 绘图调用或其他任何操作之前调用renderThread->beginTimerQuery("draw some text");,并在它之后调用renderThread->endTimerQuery();,以测量经过的 GPU 执行时间。
这里的想法是,它在测量部分之前向GPU命令队列发出命令,因此glBeginQueryTIME_ELAPSED记录了一些实现定义的计数器的值。 glEndQuery 发出一个 GPU 命令来存储当前计数与 TIME_ELAPSED 查询开头存储的计数之间的差异。该结果由 GPU 存储在查询对象中,并且在未来某个异步时间“可用”。我的代码保留已发出的计时器查询队列,并每秒检查一次以完成测量。只要队列头部的计时器查询仍然可用,我的dumpTimerQueue 就会继续打印测量值。最终它会遇到一个尚不可用的计时器并停止打印消息。
我添加了一个附加功能,它在对测量函数的 60 次调用中减少了 59 次,因此它每秒仅对我程序中的所有仪器进行测量一次。这可以防止过多的垃圾邮件并使其可用于转储到标准输出以进行开发,并防止由测量引起的过多性能干扰。这就是limiter.frame60的东西,frame60保证
虽然这不能完美地回答问题,但您可以通过记录所有绘制调用的经过时间与经过的挂钟时间来推断 GPU 使用情况。如果帧为 16 毫秒,定时器查询 TIME_ELAPSED 为 8 毫秒,您可以推断出大约 50% 的 GPU 使用率。
另外一点:测量是通过将 GPU 命令放入 GPU 队列来测量 GPU 执行时间。线程与它无关,如果那些enqueue内部的操作在一个线程中执行,那将是等效的。
【讨论】:
我从来没有见过这样的事情。通常,您会尽可能快地渲染一帧,然后对 CPU 帧进行一些后处理或预处理,然后再渲染下一帧,因此使用率会在 0% 到 100% 之间波动。很少有 FPS 被限制为最大数字,只有在这种情况下,这才是一个有意义的数字。
【讨论】: