span
在 OpenTelemetry C++ 的 sdk/src/trace 目录中,不同的 span 定义和实现是为了支持追踪(Tracing)功能的多样化需求。以下是一些关键点来帮助您理解它们的区别:
Span 的核心概念
Span 是 OpenTelemetry 中的核心追踪单元,表示一次独立的操作或事件。
每个 Span 包含以下信息:
- 操作的名称
- 上下文信息(如 TraceId 和 SpanId)
- 开始和结束时间
- 属性(Attributes)和事件(Events)
- 链接(Links)
在不同文件中的 Span 定义
在 OpenTelemetry 的代码中,Span 有不同的实现和变体,用于适应多种场景:
API 层的 Span
定义在 api/include/opentelemetry/trace/span.h。
这是一个抽象类,定义了 Span 的基本接口,如 SetAttribute、AddEvent、End 等方法。
它提供了一个标准化的接口,供所有 Span 实现遵循。
SDK 层的 Span
定义在 sdk/src/trace/span.h 和实现中(如 span.cc)。
这是 API 层 Span 的具体实现,处理真正的追踪逻辑。
它负责:
记录和存储追踪数据。
与 Tracer 和 SpanProcessor 协同工作,将数据导出到后端。
默认的空操作 Span(DefaultSpan)
定义在 api/include/opentelemetry/trace/default_span.h。
用于在没有实际追踪上下文的情况下,提供一个占位的 Span 实现。
它不会记录任何数据,仅用于传播上下文。
设计背后的原因
抽象与实现分离:
API 层的 Span 是一个接口,定义了所有 Span 的通用行为。
SDK 层的 Span 是具体实现,负责处理数据。
灵活性与扩展性:
不同的 Span 实现可以适应不同的需求,比如实际追踪、空操作实现等。
性能优化:
默认的空操作 Span 减少了不必要的开销。
API 与SDK
API 层的含义
API(Application Programming Interface)层 是为开发者提供的接口,定义了功能的规范和使用方法:
-
作用:
- 提供统一的接口,供用户或其他模块调用。
- 隐藏实现的细节,只暴露功能定义。
- 例如,
opentelemetry::trace::Span
是一个抽象接口,定义了追踪单元的核心操作(如SetAttribute
、AddEvent
等)。
-
特点:
- 抽象化:API 层主要包含接口和定义,而不涉及具体实现。
- 独立性:可以独立于具体实现运行。
- 灵活性:允许不同的实现(如 SDK 层)以自己的方式实现这些接口。
-
示例 :
在 OpenTelemetry 中,API 层定义了所有核心行为,例如
Tracer
、Span
和SpanContext
,目的是为用户提供一个统一的追踪接口。
SDK 的含义
SDK(Software Development Kit) 层是 API 的具体实现,负责实现 API 中定义的功能:
-
作用:
- 提供 API 的具体实现,包括逻辑、数据存储、导出等。
- 负责和后端服务(如 OpenTelemetry Collector)交互。
- 例如,
sdk::trace::Span
是opentelemetry::trace::Span
的实现类,用于记录和管理追踪数据。
-
特点:
- 实现性:SDK 层实现了 API 的所有功能,提供具体的逻辑和行为。
- 可扩展性:允许用户配置不同的导出器、采样器等组件。
- 面向后端:将跟踪数据导出到后端进行存储和分析。
-
示例 :
在 OpenTelemetry 中,
sdk::trace::Tracer
和sdk::trace::Span
是 API 层Tracer
和Span
的具体实现。SDK 层实现了追踪数据的记录、处理和导出。
API 层与 SDK 层的关系
- API 层 定义了功能和行为,是用户与库之间的接口。
- SDK 层 实现了 API 的功能,负责具体逻辑和与后端的交互。
- 调用关系:用户通过 API 层调用功能,而这些功能的具体实现由 SDK 层提供。
类比
可以将 API 层和 SDK 层的关系比作一个家电:
- API 层:家电的遥控器,定义了开关、调节等功能。
- SDK 层:家电的内部组件,具体实现了遥控器的功能(如启动电机、调节温度等)。
Recordable
Recordable
是 OpenTelemetry C++ SDK 中的一个核心接口,用于表示可记录的数据结构,如日志记录或追踪(Tracing)数据。这是一个抽象类,具体实现取决于导出器(Exporter)或处理器(Processor)的需求。以下是关键点:
1. Recordable
的定义
-
主要功能 :
- 提供一个标准化的数据结构,用于存储追踪或日志记录的数据。
- 定义了一些通用方法,如设置属性、添加事件和链接等。
-
代码片段 :
cppclass Recordable { public: virtual ~Recordable() = default; virtual void SetIdentity(const opentelemetry::trace::SpanContext &span_context, opentelemetry::trace::SpanId parent_span_id) noexcept = 0; virtual void SetAttribute(nostd::string_view key, const opentelemetry::common::AttributeValue &value) noexcept = 0; virtual void AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp, const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; virtual void AddLink(const opentelemetry::trace::SpanContext &span_context, const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; virtual void SetName(nostd::string_view name) noexcept = 0; virtual void SetTraceFlags(opentelemetry::trace::TraceFlags flags) noexcept = 0; virtual void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept = 0; virtual void SetDuration(std::chrono::nanoseconds duration) noexcept = 0; };
2. Recordable
的主要方法
SetIdentity
:
设置Recordable
的标识,包括SpanContext
和父级SpanId
。SetAttribute
:
设置Recordable
的属性(键值对)。AddEvent
:
添加事件(包括名称、时间戳和属性)。AddLink
:
添加链接到其他Span
。SetName
:
设置Recordable
的名称。SetTraceFlags
:
设置追踪标志。SetStartTime
和SetDuration
:
设置Recordable
的开始时间和持续时间。
3. 具体实现
Recordable
是一个抽象类,具体功能由不同的导出器实现,例如:- Zipkin 导出器 :
exporters/zipkin/recordable.h
- 实现在
opentelemetry-cpp/exporters/zipkin/src/recordable.cc
- 提供了 Zipkin 格式的实现。
- 支持 JSON 格式输出。
- OTLP 导出器 :
exporters/otlp/src/otlp_file_exporter.cc
- 提供了 OTLP 格式的实现。
- 支持 gRPC 和 HTTP 导出。
- Zipkin 导出器 :
4. 与 SpanProcessor
的关系
Recordable
是SpanProcessor
的核心工具,用于将数据从内存中提取并格式化为导出器可用的形式。- 例如:
SpanProcessor::MakeRecordable
创建Recordable
的实例。SpanProcessor::OnStart
和SpanProcessor::OnEnd
使用Recordable
来处理Span
数据。
5. 总结
Recordable
是 OpenTelemetry 的一个核心抽象,用于在追踪和日志记录数据的生命周期中充当一个可记录的数据存储单元。它的具体实现由导出器(如 Zipkin 或 OTLP)决定,提供了灵活性以支持各种后端服务。如果需要进一步理解某个导出器的实现,可以查看它的具体代码文件。
项目架构
https://opentelemetry.opendocs.io/docs/specs/otel/overview/
OpenTelemetry C++ 项目是一个实现分布式追踪和指标采集的客户端库,支持多种后端系统。它的架构分为以下几个关键组件或层次,每个组件负责特定的功能:
1. API 层
API 层是开发者直接与 OpenTelemetry 交互的接口。它定义了一组抽象类和接口,允许开发者记录追踪(Tracing)数据和指标(Metrics),而无需了解底层实现。
- 关键文件 :
opentelemetry/trace/span.h
:定义了Span
类,用于描述分布式追踪中的操作。opentelemetry/trace/tracer.h
:定义了Tracer
类,负责创建和管理Span
。opentelemetry/metrics
:用于指标的记录和管理。
- 职责 :
- 提供开发者调用的接口,例如创建
Span
、设置属性、记录事件。 - 保持实现的独立性,允许使用不同的 SDK 或后端。
- 提供开发者调用的接口,例如创建
2. SDK 层
SDK 层是 API 的具体实现,负责将追踪数据和指标从内存中提取、处理,并通过导出器(Exporter)发送到后端。
- 关键文件 :
sdk/trace/span.h
:实现了Span
的具体功能。sdk/trace/processor.h
:定义SpanProcessor
,用于处理和批处理Span
数据。sdk/metrics
:实现指标的采集和管理。
- 职责 :
- 提供
Tracer
和Span
的实现。 - 管理配置(如采样器、处理器、导出器)。
- 将数据批量发送给后端。
- 提供
3. 导出器(Exporter)
导出器负责将追踪数据和指标发送到后端系统,例如 Zipkin、Jaeger、Prometheus 或 OpenTelemetry Collector。
- 关键文件 :
exporters/zipkin
:实现 Zipkin 的导出功能。exporters/otlp
:实现 OpenTelemetry Protocol (OTLP) 的导出功能。
- 职责 :
- 将
Recordable
(可记录的数据结构)转换为后端支持的格式(如 JSON 或 Protobuf)。 - 通过 HTTP、gRPC 或文件等方式发送数据。
- 将
4. 工具与兼容性层
nostd
(Non-Standard Library) :- 提供了一些轻量级的工具类,例如
nostd::span
,用于兼容没有 C++17 支持的环境。
- 提供了一些轻量级的工具类,例如
common
:- 定义了一些通用的工具和数据结构,如属性值(AttributeValue)和时间戳(Timestamp)。
5. 测试与示例
- 测试 :
- 提供了单元测试和集成测试,确保每个模块的功能正常。
- 测试代码存放在
api/test
和sdk/test
目录中。
- 示例 :
- 提供了各种使用场景的示例,帮助开发者快速上手。
- 示例代码存放在
examples
目录中。
6. 项目目录结构
以下是项目目录结构的简要说明:
api
:API 层定义的接口和抽象类。sdk
:SDK 的具体实现,包括追踪和指标的处理。exporters
:支持的导出器模块(如 OTLP、Zipkin)。examples
:示例代码,演示如何使用 OpenTelemetry C++。common
:通用工具和数据结构。nostd
:非标准库的实现,用于兼容不同的 C++ 标准。tests
:测试代码,验证项目的正确性。
7. 架构工作流程
- 开发者调用 API :
- 使用
Tracer
创建Span
,并记录操作信息。
- 使用
- SDK 处理数据 :
SpanProcessor
处理和存储Span
数据。
- 导出器发送数据 :
- 数据通过导出器发送到后端系统。
- 后端分析 :
- 后端系统(如 Zipkin、Jaeger)接收并分析追踪数据。
总结
OpenTelemetry C++ 项目通过模块化设计,将接口、实现和导出器分离,提供了高效、灵活的分布式追踪和指标采集方案。它的架构保证了可扩展性,允许用户根据需求配置或扩展功能。如果需要更深入的说明,可以指定某个模块或文件!
学习点
OpenTelemetry C++ 项目架构设计中有许多值得学习和借鉴的地方,特别是在构建灵活、高效、可扩展的分布式追踪和指标采集系统方面。以下是一些关键学习点:
1. 模块化设计
- 特点 :
- 项目将功能分解为多个模块,包括 API 层、SDK 层、导出器(Exporter)和工具库(nostd, common)。
- 每个模块职责单一,易于维护和扩展。
- 学习点 :
- 模块化设计使代码结构清晰,并允许开发者根据需要选择性地引入模块。
- 提供了灵活性,例如用户可以仅使用 API 层而不依赖具体的 SDK 实现。
2. API 和 SDK 分离
- 特点 :
- API 层提供抽象接口,开发者可以面向接口编程。
- SDK 层实现了 API 的具体功能,并处理复杂的逻辑,如数据采样、批处理和导出。
- 学习点 :
- 这种分层设计提高了代码的灵活性和可替换性。例如,用户可以用自定义的 SDK 替换默认实现,而无需修改 API 层代码。
3. 可插拔的导出器(Exporter)
- 特点 :
- 项目支持多种导出器(如 Zipkin、Jaeger 和 OTLP),这些导出器实现了相同的接口(
SpanExporter
)。 - 用户可以根据需求选择或开发自己的导出器。
- 项目支持多种导出器(如 Zipkin、Jaeger 和 OTLP),这些导出器实现了相同的接口(
- 学习点 :
- 使用接口和工厂模式设计导出器,使系统可以轻松扩展支持新的后端。
- 通过抽象与实现分离,增强了系统的适配能力。
4. 轻量级工具库(nostd)
- 特点 :
- 提供了一些轻量级的工具类(如
nostd::span
、nostd::string_view
),以减少对外部库的依赖。 - 设计上兼容 C++11 和 C++14,适应了不同的编译环境。
- 提供了一些轻量级的工具类(如
- 学习点 :
- 在需要兼容老旧环境时,可以通过实现简化版的标准库功能来支持更多用户场景。
5. 高效的并发支持
- 特点 :
- 使用线程安全的数据结构和锁机制(如
std::mutex
)来保证多线程环境下的安全性。 - 例如,
BatchSpanProcessor
使用缓冲区和后台线程来批量导出数据,减少了对性能的影响。
- 使用线程安全的数据结构和锁机制(如
- 学习点 :
- 在高性能应用中,通过批处理和异步操作可以有效减少运行时开销。
- 对并发访问进行严格的控制,避免数据竞争问题。
6. 灵活的配置和扩展机制
- 特点 :
- 支持用户自定义配置(如采样器、处理器和导出器)。
- 提供了多种默认实现(如简单采样器和批处理器),同时允许用户扩展自己的实现。
- 学习点 :
- 提供默认配置降低了用户的学习成本,而开放扩展接口又满足了高级用户的需求。
- 使用工厂模式生成对象(如
SpanProcessor::MakeRecordable
),进一步增强了灵活性。
7. 跨语言的设计一致性
- 特点 :
- OpenTelemetry 是一个跨语言的项目,C++ 实现遵循了 OpenTelemetry 的整体设计规范,与其他语言(如 Go、Python)的实现保持一致。
- 学习点 :
- 在多语言项目中,保持设计一致性有助于跨团队协作,并降低用户在不同语言中的学习成本。
8. 清晰的目录结构和文档
- 特点 :
- 项目目录按照功能划分,如
api
、sdk
、exporters
、examples
和tests
。 - 提供了详细的文档和代码示例,帮助用户快速上手。
- 项目目录按照功能划分,如
- 学习点 :
- 清晰的目录结构和完善的文档提升了项目的可维护性和可用性。
9. 测试覆盖与质量保证
- 特点 :
- 提供了丰富的单元测试和集成测试,保证了模块的正确性。
- 测试覆盖了常见场景和边界情况。
- 学习点 :
- 测试是高质量代码的保障,特别是在分布式系统中,测试可以帮助捕获潜在的复杂问题。
10. 遵循最佳实践和开源规范
- 特点 :
- 遵循 Apache 2.0 开源协议,代码风格整洁,注释清晰。
- 使用 CMake 管理构建,支持多平台编译。
- 学习点 :
- 遵循开源规范和最佳实践可以提升项目的可读性和社区参与度。
总结
OpenTelemetry C++ 项目的架构体现了模块化、灵活性和高性能的特点。这种设计值得学习,特别是在开发分布式系统、异步处理和可扩展性框架时,可以借鉴其 API/SDK 分离、可插拔设计和高效的并发处理等技术思想。
工厂模式
在 OpenTelemetry C++ 项目中,导出器(Exporter)的设计使用了接口和工厂模式,这种设计模式有助于实现模块化、灵活性和可扩展性。以下是具体解释:
1. 接口的使用
- 定义:接口是一种抽象类,提供了一组方法的定义,而不包含具体实现。
- 在导出器中的作用 :
- 导出器实现了
SpanExporter
接口,该接口定义了导出追踪数据的标准方法。 - 用户可以通过使用接口操作导出器,而无需关心其具体实现。
- 导出器实现了
- 关键接口 :
-
SpanExporter
(位于sdk/include/opentelemetry/sdk/trace/exporter.h
):cppclass SpanExporter { public: virtual ~SpanExporter() = default; virtual std::unique_ptr<Recordable> MakeRecordable() noexcept = 0; virtual ExportResult Export( const nostd::span<std::unique_ptr<Recordable>> &spans) noexcept = 0; virtual bool Shutdown(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; };
MakeRecordable
:创建一个Recordable
(可记录的数据结构)。Export
:将一批追踪数据导出到后端。Shutdown
:关闭导出器并完成清理工作。
-
2. 工厂模式的使用
- 定义 :工厂模式提供了一种创建对象的方式,用户只需要调用工厂方法,而不需要直接使用
new
操作符。 - 在导出器中的作用 :
- 工厂模式用于创建导出器实例,如 Zipkin、OTLP 等。
- 用户可以通过工厂方法动态选择导出器的类型,而无需修改代码。
- 示例 :
-
在某些导出器实现中,
MakeRecordable
方法就是一种工厂方法,用于生成Recordable
对象。例如:cppstd::unique_ptr<sdk::trace::Recordable> ZipkinExporter::MakeRecordable() noexcept { return std::unique_ptr<sdk::trace::Recordable>(new ZipkinRecordable()); }
- 这里
ZipkinExporter
使用工厂方法创建一个ZipkinRecordable
对象,而用户无需直接调用new
。
- 这里
-
3. 设计的优势
- 模块化 :
- 不同的导出器(如 OTLP、Zipkin)实现了相同的接口,因此可以很容易替换或扩展新的导出器。
- 灵活性 :
- 工厂模式允许动态创建对象,用户可以根据配置选择导出器类型,而无需修改代码。
- 可维护性 :
- 通过接口分离定义和实现,降低了模块之间的耦合性,便于维护和测试。
- 可扩展性 :
- 新的导出器只需实现接口,并注册到工厂中即可,无需修改现有代码。
4. 实际使用案例
假设用户想使用 Zipkin 导出器:
- 接口操作 :
用户调用SpanExporter
定义的方法,例如Export
。 - 工厂创建 :
Zipkin 导出器通过工厂方法MakeRecordable
创建Recordable
对象,并将其导出为后端可识别的数据格式。
总结
通过使用接口和工厂模式,OpenTelemetry C++ 项目实现了导出器的模块化和灵活性。接口定义了功能规范,工厂模式提供了动态创建对象的能力,使得用户可以轻松扩展和维护系统,同时支持多种后端。
高并发
OpenTelemetry C++ 项目在高并发环境下的设计,重点在于高效的数据收集、线程安全性以及批量处理机制。以下是对其高并发设计的详细分析:
1. 高效的并发数据收集机制
-
Span
的线程安全设计- 在多线程环境下,多个
Span
可能同时被创建和记录数据。 Span
的实现中使用了线程安全的机制(如std::mutex
),以确保在高并发环境下数据一致性。- 例如在
sdk::trace::Span
的实现中,使用了mutable std::mutex
锁来保护共享资源。
- 在多线程环境下,多个
-
Recordable
的抽象Recordable
是一个线程兼容的类,用于存储追踪数据。- 通过接口隔离数据操作逻辑,避免了直接操作共享资源所引发的竞争。
2. 批处理与异步处理
高并发系统中,频繁导出数据会导致性能下降,因此 OpenTelemetry C++ 使用批处理和异步处理来优化性能。
-
批处理(Batch Processing)
- OpenTelemetry 提供了
BatchSpanProcessor
,用于将多个Span
数据合并到批次中,再统一导出。 - 关键点 :
- 使用了内部缓冲区(如循环队列)存储待导出的
Span
。 - 当缓冲区达到一定大小或超时时,会触发批量导出。
- 使用了内部缓冲区(如循环队列)存储待导出的
- 优点 :
- 减少了对导出器的调用频率,降低了后端系统的压力。
- 提高了网络和处理的吞吐量。
- OpenTelemetry 提供了
-
异步处理
BatchSpanProcessor
使用后台线程异步处理数据:- 一个独立的线程定期检查缓冲区,批量发送数据到导出器。
- 使用条件变量(
std::condition_variable
)来唤醒工作线程,避免忙等(busy-waiting)。
- 优点 :
- 避免了主线程阻塞,提高了整体系统的响应能力。
- 允许高并发的
Span
记录,不会受到导出操作的影响。
3. 线程安全的共享资源管理
-
锁的使用
- 在需要修改共享资源时(例如缓冲区或
Span
的属性),使用了std::mutex
或其他同步原语来保护资源。 - 例如,
BatchSpanProcessor
的缓冲区操作实现是线程安全的。
- 在需要修改共享资源时(例如缓冲区或
-
无锁设计
- 某些场景中,使用了无锁设计来减少锁竞争:
- 例如,在
Recordable
的创建和记录中,尽量减少对全局状态的依赖。 - 避免了频繁的上下文切换和锁等待,提高了并发性能。
- 例如,在
- 某些场景中,使用了无锁设计来减少锁竞争:
4. 线程池和资源分配
- 专用线程
BatchSpanProcessor
以及其他批处理器会启动专用线程处理异步任务,而不会影响主线程的性能。
- 线程池扩展
- 项目为高负载环境提供了灵活的扩展能力,用户可以通过配置调整线程池的大小或后台线程的行为。
5. 内存优化与延迟控制
高并发场景下,内存使用和延迟控制至关重要。
-
缓冲区限制
- 每个
SpanProcessor
都有最大队列大小和最大批量大小的限制,防止内存溢出。 - 当缓冲区满时,旧数据可能会被丢弃,从而保证系统的稳定性。
- 每个
-
超时控制
BatchSpanProcessor
支持自定义超时时间,当导出操作超过指定时间时会被强制终止。- 这避免了后台线程因单次导出失败而阻塞。
6. 高并发设计的优势总结
-
高效的数据收集和处理:
- 多线程环境下,
Span
和Recordable
的线程安全设计确保了数据一致性。 - 批处理和异步导出减少了导出器的调用频率,提高了整体吞吐量。
- 多线程环境下,
-
资源分配合理:
- 使用独立的后台线程,隔离了数据收集和导出的任务,避免相互影响。
-
灵活的配置和扩展:
- 用户可以通过调整批处理大小、队列限制和线程数等参数,适应不同的高并发场景。
-
可维护性和可扩展性:
- 通过接口和抽象层分离实现,允许用户替换或扩展不同的处理器和导出器。
学习启发
OpenTelemetry C++ 项目在高并发设计中的关键思想包括:
- 线程安全:在共享资源上使用合理的同步机制。
- 异步与批处理:减少阻塞操作,提高系统性能。
- 模块化设计:通过接口和抽象分离逻辑,增强扩展性。
- 灵活配置:允许用户根据实际需求调整系统行为。
这些设计思路可以广泛应用于高性能和高并发的系统开发中。
跨语言的设计一致性
https://opentelemetry.io/docs/concepts/signals/traces/#spans
QueryPerformanceFrequency
CSCI 4717 -- High Performance Counter
High Resolution Counter
Let's play around with the high resolution counter again. Remember that this counter counts the number of clock cycles of the system clock since the system was last powered up. The function we use to read this timer is QueryPerformanceCounter(). Since function reads the processor's high resolution timer which is incremented once with every clock pulse of the processor's clock, then for a 3.59 GHz machine, this timer is incremented approximately 3,590,000,000 times in a second. QueryPerformanceCounter() takes as its single parameter a pointer to a LARGE_INTEGER which you define in your code. This gives us a way to read how many clock cycles have occurred since the machine was turned on.
cpp
LARGE_INTEGER current_time;
QueryPerformanceCounter(¤t_time);
The number returned by QueryPerformanceCounter() is huge and not very useful for timing at the "seconds" level. As I mentioned earlier, it is incremented once with every pulse of the system clock. If we knew the system clock frequency, we could figure out how many seconds had passed since turning on the machine. This can be done with QueryPerformanceFrequency(). This function returns the frequency of the processor once again using a LARGE_INTEGER passed as a pointer for the function's only parameter.
cpp
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
Dividing the counter value by the frequency will convert the time to seconds. Dividing the counter value by the frequency divided by 1,000 will convert the time to milliseconds. Dividing the counter value by the frequency divided by 1,000,000 will convert the time to microseconds and so on.
Laboratory Exercise
In today's exercise, we will be using the counter to measure time. For the most part, we will be measuring time in microseconds. Begin by creating a console application in Visual Studio 2005. Below is some sample code that should work well for a template as we go through some of these examples. Remember that LARGE_INTEGER is a UNION, so to access the value stored in it, you must define the way you want the data returned. To do this, use the QuadPart component.
cpp
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include "Windows.h"
using namespace std;
int main( )
{
LARGE_INTEGER current, frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(¤t);
cout << current.QuadPart/(frequency.QuadPart/1000000) << " uS have passed since resetting the high performance counter.\n";
return 0;
}
Today, we will see if we can get some semi-accurate measurements of the following programming functions.
Time to read the high performance counter
Time to perform a function call
Time to return from a function call
Time between interruptions from the O/S
Comparison between times to call cout and printf
Comparison between times to perform recursive and non-recursive functions
C++ Windows时间函数 QueryPerformanceCounter()与QueryPerformanceFrequency()