Skip to content

Latest commit

 

History

History
199 lines (150 loc) · 9.65 KB

building-with-stdlib.md

File metadata and controls

199 lines (150 loc) · 9.65 KB

Building with Standard C++ Library

Standard Library build flavor works best for statically linking the SDK in a process (environment where ABI compat is not a requirement), or for "header-only" implementation of SDK.

Proposed approach cannot be employed for shared libs in environments where ABI compatibility is required. OpenTelemetry SDK binary compiled with compiler A + STL B will not be ABI -compatible with the main executable compiled with compiler C + STL D.

In addition to standard library, similar approach can be reused to implement the API surface classes with Abseil classes instead of nostd, in products that prefer Abseil.

Motivation

nostd classes in OpenTelemetry API were introduced for the following reasons:

  • ABI stability: scenario where different modules are compiled with different compiler and incompatible standard library.
  • backport of C++17 and above features to C++11 compiler.

The need for custom nostd classes is significantly diminished when the SDK is compiled with C++17 or above compiler. Only std::span needs to be backported.

Subsequently, there is no need for nostd classes in environment with C++20 or above compiler, where all system modules are compiled with the same / compatible compiler. And where the same standard library implementation is being used. This is the case when SDK is compiled into product itself, with no runtime loadable components.

Compiling OpenTelemetry SDK from source using standard library classes: std::map, std::string_view, std::span, std::variant instead of nostd:: yields better performance and debugability at expense of potentially losing ABI compatibility. However, the standard library built for Release is guaranteed to be compatible across Visual Studio 2015, 2017 and 2019 compilers on Windows with vc14x runtime. Thus, ABI stability requirement introduces an additional unnecessary runtime complexity and overhead.

While we are committed to support nostd classes for those environments where ABI compatibility is a requirement, we would also like to add flexibility to build system to optimize the SDK for the case where ABI compatibility is NOT a requirement.

Implementation of this feature can be subsequently be used as a foundation for further work - allow bindings to Abseil "backport" implementation of the standard library.

Implementation is completely opaque from SDK code / SDK developer perspective: mapping / aliasing from nostd:: classes back to their std:: counterparts is done in a corresponding opentelemetry/nostd/*.h header. Users still use nostd classes, but the most optimal implementation is picked up depending on whether users require ABI stability or not.

Example environments that contain the full set of standard classes:

  • C++17 or above compiler, with Microsoft GSL backport of gsl::span
  • C++20 compilers: Visual Studio 2019+, latest LLVM clang, latest gcc

We continue fully supporting both models (nostd, stdlib) by running CI for both.

Implementation

Allow to alias from nostd:: to std:: classes for C++17 and above.

Consistent handling of std::variant across various OS:

  • backport of a few missing variant features, e.g. std::get and std::visit for older version of Mac OS X. Patches that enable proper handling of std::visit and std::variant irrespective of OS version to resolve this quirk.

  • ability to borrow implementation of C++20 gsl::span from Microsoft Guidelines Support Library. This is necessary for C++17 and above compiler.

  • ability to use Abseil classes for Visual Studio 2015 :nostd::variant does not compile with Visual Studio 2010. Please refer to this issue

Pros and Cons

Using trusted/certified Standard Library

Using standard library implementation classes in customer code and on API surface instead of nostd classes. Certain environments would prefer to have standard instead of non-standard classes due to security, performance, stability and debug'ability considerations. For example, Visual Studio IDE provides 1st class Debug experience for Standard containers, plus additional runtime-checks for Debug builds that use Standard containers.

Minimizing binary size

No need to marshal types from standard to nostd, then back to standard library across ABI boundary - means less code involved and less memcpy. We use Standard Library classes used elsewhere in the app. We can optimize the event passing by avoiding KeyValueIterable transform (and, thus, unnecessary memcpy) when we know that the incoming container type matches that one used by SDK.

Avoiding unnecessary extra memcpy (perf improvements)

No need to transform from 'native' standard library types, e.g. std::map via KeyValueIterable means we can bypass that transformation process, if and when we know that the ABI compatibility is not a requirement in certain environment. ETA perf improvement is 1.5%-3% better perf since an extra transform, memcpy, iteration for-each key-value is avoided. Custom OpenTelemetry SDK implementation may operate on standard container classes rather than doing transform from one container type to another.

ABI stability

Obviously this approach does not work in Linux environments, where ABI stability guarantee must be provided, e.g. for dynamically loadable OpenTelemetry SDK module and plugins for products such as NGINX, Envoy, etc. Example: a product is compiled with modern gcc compiler. But OpenTelemetry SDK is compiled with an older runtime library. In this case the SDK must be compiled with nostd.

Note that for most scenarios with modern Windows compilers, STL library is ABI-safe across Visual Studio 2015, 2017 and 2019 with vc14x runtime.

Quote from official documentation :

Visual Studio 2015, 2017, and 2019: the runtime libraries and apps compiled by any of these versions of the compiler are binary-compatible. It's reflected in the C++ toolset major number, which is 14 for all three versions. The toolset version is v140 for Visual Studio 2015, v141 for 2017, and v142 for 2019. Say you have third-party libraries built by Visual Studio 2015. You can still use them in an application built by Visual Studio 2017 or 2019. There's no need to recompile with a matching toolset. The latest version of the Microsoft Visual C++ Redistributable package (the Redistributable) works for all of them.

Visual Studio provides 1st class debug experience for the standard library.

Build and Test considerations

Separate flavors of SDK build

Supported build flavors:

  • nostd - OpenTelemetry backport of classes for C++11. Not using standard lib.
  • stdlib - Standard Library. Full native experience with C++20 compiler. C++17 works but with additional dependencies, e.g. either MS-GSL or Abseil for std::span implementation (gsl::span or absl::Span).
  • absl - TODO: this should allow using Abseil C++ library only (no MS-GSL).

Currently only nostd and stdlib configurations are implemented in CMake build. absl is reserved for future use. Build systems other than CMake need to #define HAVE_CPP_STDLIB to enable the Standard Library classes.

Build matrix

List of compilers that support building with standard library classes:

Compiler Language standard Notes
Visual Studio 2015 C++14 can only be built with nostd flavor
Visual Studio 2017 C++17 requires gsl::span for std::span implementation
Visual Studio 2019 C++20
Xcode 11.x C++17 requires gsl::span for std::span implementation
Xcode 12.x C++20
gcc-7 C++17 requires gsl::span for std::span implementation
gcc-9+ C++20

C++20 std::span -compatible implementation is needed for C++17 compilers.

Other modern C++ language features used by OpenTelemetry, e.g. std::string_view and std::variant are available in C++17 standard library. Minor customization needed for Apple LLVM clang in std::variant exception handling due to the fact that the variant exception handler presence depends on what OS version developer is targeting. This exception on Apple systems is implemented inside the OS system library. This idiosyncrasy is now handled by OpenTelemetry API in opaque manner: support nostd::variant exception handling even on older Mac OS X and iOS by providing implementation of it in OpenTelemetry SDK: if exceptions are enabled, then throw nostd::bad_variant_access exception old OS where std::bad_variant_access is unavailable.

Note on gsl::span vs absl::Span

It is important to note that, while absl::Span is similar in design and purpose to the std::span (and existing gsl::span reference implementation), absl::Span is not currently guaranteeing to be a drop-in replacement for any eventual standard. Instead, absl::Span aims to have an interface as similar as possible to absl::string_view, without the string-specific functionality.

Thus, OpenTelemetry built with standard library prefers gsl::span as it is fully compatible with standard std::span. It may become possible to use Abseil classes should we confirm that its behavior is consistent with nostd::span expectations.

Test setup

CI allows to validate that all OpenTelemetry functionality is working the same identical way irrespective of what C++ runtime / STL library it is compiled with.