-
Notifications
You must be signed in to change notification settings - Fork 449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[EXPORTER] Optimize OTLP HTTP compression #3178
Conversation
✅ Deploy Preview for opentelemetry-cpp-api-docs canceled.
|
|
||
// ZLIB: Have to maually specify 16 bits for the Gzip headers | ||
const int window_bits = 15 + 16; | ||
static constexpr int kWindowBits = MAX_WBITS + 16; | ||
static constexpr int kMemLevel = MAX_MEM_LEVEL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, MAX_MEM_LEVEL appears to be 9 and, from the official documentation
The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8.
The default level is not exposed, unfortunately (header zutil.h not available for including), but the code to drive the default is:
#if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
#else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
/* default memLevel */
My intuition was to not hardcode the "default" value 8 and use the max level setting provided by the macro but I can revert this back to a hardcoded value of 8 as in the current compression code.
{ | ||
if (callback) | ||
{ | ||
callback->OnEvent(opentelemetry::ext::http::client::SessionState::CreateFailed, ""); | ||
callback->OnEvent(opentelemetry::ext::http::client::SessionState::CreateFailed, | ||
zs.msg ? zs.msg : ""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a precaution; the documentation states that msg
is null when there are no errors. Given that we check for an error return code before even attempting to use this variable, the ternary if is probably redundant, but it does not hurt keeping it as another safety net in case the invariant is broken by the custom deflateInPlace code or future versions of the library, for instance.
The list of possible messages to be reported:
z_const char * const z_errmsg[10] = {
(z_const char *)"need dictionary", /* Z_NEED_DICT 2 */
(z_const char *)"stream end", /* Z_STREAM_END 1 */
(z_const char *)"", /* Z_OK 0 */
(z_const char *)"file error", /* Z_ERRNO (-1) */
(z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */
(z_const char *)"data error", /* Z_DATA_ERROR (-3) */
(z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */
(z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */
(z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */
(z_const char *)""
};
http_request_->SetBody(compressed_body); | ||
auto size = static_cast<uInt>(http_request_->body_.size()); | ||
auto max = size; | ||
stream = deflateInPlace(&zs, http_request_->body_.data(), size, &max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, max == size
which means that, if I interpret the comments from the library author correctly, when there is a lot of incompressible data, an auxiliary buffer is used to complete the deflate operation so that it can report the current max
value and bail out with an error code. My understanding is that this would require calling deflateInPlace
again, this time with an input buffer that has been resized to accommodate for the expected output, which would be larger than the original payload.
6ffd4c2
to
f9d7383
Compare
f9d7383
to
ed7d92b
Compare
// if we can, copy the temporary output data to the consumed portion of the input buffer, and then | ||
// continue to write up to the start of the consumed input for as long as possible | ||
auto have = strm->next_out - temp.data(); // number of bytes in temp[] | ||
if (have <= (strm->avail_in ? len - strm->avail_in : *max)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my understandarding, compare a signed interger with a unsigned integer will cause a warning. No idea why this warning does not be triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ENABLE_OTLP_COMPRESSION_PREVIEW
to work, add -DWITH_OTLP_HTTP_COMPRESSION=ON
to CMake in the maintainer builds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not seen compile warnings related to this but. indeed, the type of have here is ptrdiff_t
. At first glance, it does not seem possible that the logic yield a negative number so it could but made unsigned but, given that L96 uses it to advance through the buffer, I think it is more reasonable to keep its intended type and to cast the right hand side of condition in L93 instead.
…telemetry-cpp into ImproveCompression
13073b1
to
34bd0bd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for the fix.
Waiting on @owent to clear the change needed flag and approve. |
LGTM, but I'm not familiar with zlib APIs.I'm not sure about the details of the usage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocking merge, to resolve concern with GzipIncompressibleData
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for the fix.
Great work.
Fixes #2570
Changes
Another alternative to in-place compression was also considered, which consists in statically allocating the vector that would be used as output to deflate (i.e.
compressed_body
in the original code) and swapping it with the original body after deflate was done, essentially, reusing the original body vector for the subsequent call toSendRequest
.The very minimal benchmark showed that the performance was not that far off compared to the in-place version from this PR, but it would obviously fall short in more realistic scenarios, since it will likely require resizing this scratch vector every now and then if the input body being processed is larger.
For significant contributions please make sure you have completed the following items:
CHANGELOG.md
updated for non-trivial changes