cpp/writer/TimestampTruncatingVisitor.cpp (52 lines of code) (raw):

/** * Copyright 2004-present, Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <cassert> #include <cstring> #include <profilo/writer/TimestampTruncatingVisitor.h> namespace facebook { namespace profilo { namespace writer { // Multiplication of two 64-bit numbers, keeping only the top 64 bits. This // is necessary for the reciprocal multiplication optimization (see below). // This could be simplified with __uint128_t, but unfortunately we don't have // that type. If somehow/sometime it ever becomes available, we can get rid // of this function and perform the multiplication directly. uint64_t mulhi(uint64_t a, uint64_t b) { uint64_t a_lo = (uint32_t)a; uint64_t a_hi = a >> 32; uint64_t b_lo = (uint32_t)b; uint64_t b_hi = b >> 32; uint64_t a_x_b_hi = a_hi * b_hi; uint64_t a_x_b_mid = a_hi * b_lo; uint64_t b_x_a_mid = b_hi * a_lo; uint64_t a_x_b_lo = a_lo * b_lo; uint64_t carry_bit = ((uint64_t)(uint32_t)a_x_b_mid + (uint64_t)(uint32_t)b_x_a_mid + (a_x_b_lo >> 32)) >> 32; uint64_t multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit; return multhi; } // Optimization to divide by 1000. // See https://homepage.divms.uiowa.edu/~jones/bcd/divide.html // In short, the insight is that it's faster to multiply by the reciprocal // of a number than divide by it. In this case, the reciprocal of 1000 // is 0.001, which in fixed point notation is 0x4189374bc6a7f4 (with a // 64 bit shift). inline uint64_t div_1000(uint64_t num) { static constexpr uint64_t divisor = 0x4189374bc6a7f4; return mulhi(num, divisor); } TimestampTruncatingVisitor::TimestampTruncatingVisitor( EntryVisitor& delegate, size_t precision) : delegate_(delegate) { assert(precision == 6); } template <class T> T TimestampTruncatingVisitor::truncateTimestamp(const T& entry) { T copied(entry); // This 500 comes from the denominator (1000) divided by 2. The math here is // (a + b/2) / b = (2a + b)/2b = a/b + 1/2 = round(a/b). // The denominator is always 1000 because that's what we use to truncate // ns timestamps into us. copied.timestamp = div_1000(copied.timestamp + 500); return copied; } void TimestampTruncatingVisitor::visit(const StandardEntry& entry) { auto std_entry = entry; delegate_.visit(truncateTimestamp(std_entry)); } void TimestampTruncatingVisitor::visit(const FramesEntry& entry) { auto frames_entry = entry; delegate_.visit(truncateTimestamp(frames_entry)); } void TimestampTruncatingVisitor::visit(const BytesEntry& entry) { delegate_.visit(entry); } } // namespace writer } // namespace profilo } // namespace facebook