-
Notifications
You must be signed in to change notification settings - Fork 7
VPLAY-12274 ewma algorithm testing #808
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
Conversation
Reason for Change: test code for profiling abr algorithms Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
This PR adds a new standalone throughput estimator tool for profiling and testing EWMA (Exponentially Weighted Moving Average) algorithms used in Adaptive Bitrate (ABR) streaming. The tool downloads media segments repeatedly to collect baseline performance data and test throughput estimation algorithms.
Key changes:
- Implementation of EWMA-based throughput estimation for ABR
- Mid-download underflow prediction logic
- Performance profiling capabilities for network downloads
Reviewed changes
Copilot reviewed 5 out of 7 changed files in this pull request and generated 20 comments.
Show a summary per file
| File | Description |
|---|---|
| throughput_estimator/throughput_estimator/main.cpp | New C++17 profiling tool implementing EWMA throughput estimation, transfer statistics tracking, and mid-download abort logic for testing ABR algorithms |
| throughput_estimator/throughput_estimator.xcodeproj/xcuserdata/.../*.plist | User-specific Xcode configuration files (should not be committed) |
| throughput_estimator/throughput_estimator.xcodeproj/xcuserdata/.../Breakpoints_v2.xcbkptlist | User-specific Xcode breakpoint configuration (should not be committed) |
| throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/xcuserdata/.../UserInterfaceState.xcuserstate | User-specific Xcode UI state file (should not be committed) |
| throughput_estimator/throughput_estimator.xcodeproj/xcshareddata/xcschemes/throughput_estimator.xcscheme | Xcode build scheme configuration for the throughput_estimator target |
| throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata | Xcode workspace metadata file |
| throughput_estimator/throughput_estimator.xcodeproj/project.pbxproj | Xcode project file defining build settings, targets, and dependencies |
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
...proj/xcuserdata/pstrof200@cable.comcast.com.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
Outdated
Show resolved
Hide resolved
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 16 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
…/pstrof200@cable.comcast.com.xcuserdatad/xcschemes/xcschememanagement.plist
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.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 13 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
… removing libcurl dependencies Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 16 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 12 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
| class DownloadContext | ||
| { | ||
| private: | ||
| FILE *f = NULL; // logging | ||
| static constexpr double ewma_short_window_weight = 0.4; | ||
| double ewma_bytes_per_second = 0.0; | ||
| size_t dlnow_prev = 0; | ||
| double time_prev = 0.0; | ||
|
|
||
| public: | ||
| explicit DownloadContext( FILE *f ); | ||
|
|
||
| /** | ||
| * @param dltotal total bytes to download | ||
| * @param dlnow downloaded bytes so far | ||
| */ | ||
| int xferinfo( size_t dltotal, size_t dlnow ); | ||
| }; |
Copilot
AI
Jan 1, 2026
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.
The DownloadContext class stores a raw FILE pointer as a member without ownership semantics. This violates RAII principles and makes the class difficult to use safely. The class should either own the FILE* (and close it in the destructor) or use a non-owning observer pattern with clear documentation. Consider using std::ofstream instead or at minimum, document the ownership semantics clearly.
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
…ded to curlInfo Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 8 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.hpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
throughput_estimator/throughput_estimator/NetworkBandwidthEstimator.cpp
Outdated
Show resolved
Hide resolved
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
|
@pstroffolino I've opened a new pull request, #814, to work on those changes. Once the pull request is ready, I'll request review from you. |
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 14 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
| # If not stated otherwise in this file or this component's license file the | ||
| # following copyright and licenses apply: | ||
| # | ||
| # Copyright 2023 RDK Management |
Copilot
AI
Jan 2, 2026
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.
The copyright year should be 2026 to match the other new files in this PR. Currently shows 2023.
| # Copyright 2023 RDK Management | |
| # Copyright 2026 RDK Management |
throughput_estimator/throughput_estimator.xcodeproj/project.pbxproj
Outdated
Show resolved
Hide resolved
| isa = PBXProject; | ||
| attributes = { | ||
| BuildIndependentTargetsInParallel = 1; | ||
| LastUpgradeCheck = 2620; |
Copilot
AI
Jan 2, 2026
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.
The 'LastUpgradeCheck' is set to 2620, which appears to be an invalid Xcode version number. Xcode versions follow a pattern like 1420 (for Xcode 14.2). This should be corrected to a valid version number.
| LastUpgradeCheck = 2620; | |
| LastUpgradeCheck = 1420; |
abr/NetworkBandwidthEstimator.cpp
Outdated
| } | ||
|
|
||
| Sample::Sample( const CurlInfo &curlInfo ) : m_curlInfo(curlInfo) | ||
| { // compute derived values |
Copilot
AI
Jan 2, 2026
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 comment contains inconsistent formatting. There's a space after the opening brace on line 83 ("{ // compute derived values"), but on line 64 the same pattern uses a different style. Consider using consistent comment placement throughout the file - either always on the same line after braces or always on a separate line before.
| { // compute derived values | |
| { | |
| // compute derived values |
| GCC_WARN_UNUSED_FUNCTION = YES; | ||
| GCC_WARN_UNUSED_VARIABLE = YES; | ||
| LOCALIZATION_PREFERS_STRING_CATALOGS = YES; | ||
| MACOSX_DEPLOYMENT_TARGET = 26.2; |
Copilot
AI
Jan 2, 2026
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.
The macOS deployment target is set to 26.2 in the Release configuration as well, which is invalid. This should be a proper macOS version number like 11.0 or later.
| MACOSX_DEPLOYMENT_TARGET = 26.2; | |
| MACOSX_DEPLOYMENT_TARGET = 11.0; |
abr/NetworkBandwidthEstimator.cpp
Outdated
| #include "NetworkBandwidthEstimator.h" | ||
|
|
||
| static const double epsilon = 1e-6; | ||
| static const double BLEND_WEIGHT_HARMONIC = 0.6; // 60% harmonic, 40% EWMA |
Copilot
AI
Jan 2, 2026
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.
The variable 'BLEND_WEIGHT_HARMONIC' should be declared as 'constexpr' instead of 'const' since it's a compile-time constant. This is a C++11 best practice that allows the value to be used in more contexts and communicates intent more clearly.
throughput_estimator/main.cpp
Outdated
| #include <string> | ||
| #include <iomanip> | ||
|
|
||
| std::ofstream f_EWMA; |
Copilot
AI
Jan 2, 2026
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.
The global file stream 'f_EWMA' violates the principle of avoiding global mutable state. Consider refactoring this to be passed as a parameter or encapsulated in a class to improve testability and maintainability.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 15 comments.
Files not reviewed (1)
- throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
| TEST_F(FunctionalTests, MedianTest) | ||
| { | ||
| std::vector<double> values; | ||
| EXPECT_EQ( GetMedian(values), 0.0 ); | ||
| values.push_back(10.0); | ||
| EXPECT_EQ( GetMedian(values), 10.0 ); | ||
| values.push_back(30.0); | ||
| EXPECT_EQ( GetMedian(values), 20.0 ); | ||
| values.push_back(30.0); | ||
| EXPECT_EQ( GetMedian(values), 30.0 ); | ||
| values.push_back(20.0); | ||
| EXPECT_EQ( GetMedian(values), 25.0 ); | ||
| } |
Copilot
AI
Jan 2, 2026
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.
Missing test coverage for boundary conditions. The MedianTest should include edge cases such as negative values, very large values, NaN, and infinity to ensure robust handling of edge cases in the GetMedian function.
| int main(int argc, char** argv) | ||
| { | ||
| testing::InitGoogleTest(&argc, argv); | ||
| return RUN_ALL_TESTS(); | ||
| } |
Copilot
AI
Jan 2, 2026
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.
Empty test main function. The NetworkBandwidthEstimatorTests.cpp file only contains a test main() function but no actual test cases. Either add test cases to this file or remove it if all tests are in FunctionalTests.cpp, as Google Test can auto-generate the main function.
| curl_off_t dltotal, curl_off_t dlnow, | ||
| curl_off_t ultotal, curl_off_t ulnow) | ||
| { | ||
| DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp); |
Copilot
AI
Jan 2, 2026
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.
Avoid reinterpret_cast when static_cast is sufficient. The conversion from void* to DownloadContext* should use static_cast instead of reinterpret_cast, as the pointer type is known at compile time and this cast is type-safe.
| DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp); | |
| DownloadContext *context = static_cast<DownloadContext*>(clientp); |
| CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
| COPY_PHASE_STRIP = NO; | ||
| DEBUG_INFORMATION_FORMAT = dwarf; | ||
| DEVELOPMENT_TEAM = C9XTLYAG8J; |
Copilot
AI
Jan 2, 2026
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.
Hardcoded development team identifier. The DEVELOPMENT_TEAM is set to "C9XTLYAG8J" which appears to be a personal or specific team identifier. This should either be removed to allow developers to use their own team settings, or moved to a local configuration file that is not committed to version control.
| DEVELOPMENT_TEAM = C9XTLYAG8J; |
| static constexpr double ALPHA_SLOW = 0.2; | ||
|
|
||
| // Harmonic mean over last N samples | ||
| // Harmonic mean over last N samples. |
Copilot
AI
Jan 2, 2026
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.
Duplicate comment detected. This line is identical to line 111 and should be removed to maintain clean documentation.
| // Harmonic mean over last N samples. |
| void NetworkBandwidthEstimator::RecomputeHarmonicMeanAndMedianTTFB() | ||
| { // Overhead = median TTFB from all samples | ||
| std::vector<double> ttfb; | ||
| ttfb.reserve(m_history.size()); | ||
| for( const auto& s : m_history ) | ||
| { | ||
| ttfb.push_back(s.GetTimeToFirstByteSeconds() ); | ||
| } | ||
| m_estimated_TTFB_seconds = GetMedian(ttfb); | ||
|
|
||
| // Harmonic mean of throughput over last harmonic_window samples | ||
| const size_t n = m_history.size(); | ||
| const size_t start = (n > harmonic_window) ? (n - harmonic_window) : 0; | ||
| double denominator = 0.0; | ||
| size_t count = 0; | ||
| for( size_t i = start; i < n; i++ ) | ||
| { | ||
| const double payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond(); | ||
| if( payloadBytesPerSecond > epsilon ) | ||
| { | ||
| denominator += 1.0/payloadBytesPerSecond; | ||
| count++; | ||
| } | ||
| } | ||
| m_harmonic_BytesPerSecond = (count > 0 && denominator > 0.0) ? (static_cast<double>(count) / denominator) : 0.0; |
Copilot
AI
Jan 2, 2026
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.
Inconsistent namespace qualification. The function uses std::size_t for some variables (lines 61, 62) but size_t without std:: for others (line 119, 121, 122). Maintain consistency by using std::size_t throughout the function.
throughput_estimator/main.cpp
Outdated
| curl_off_t ultotal, curl_off_t ulnow) | ||
| { | ||
| double now = GetCurrentTimeMonotonicSeconds(); | ||
| DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp); |
Copilot
AI
Jan 2, 2026
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.
Avoid reinterpret_cast when static_cast is sufficient. The conversion from void* to DownloadContext* should use static_cast instead of reinterpret_cast, as the pointer type is known at compile time and this cast is type-safe.
| DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp); | |
| DownloadContext *context = static_cast<DownloadContext*>(clientp); |
| m_history.emplace_back(sample); | ||
|
|
||
| if (m_history.size() > MAX_HISTORY) | ||
| { // Trim history to avoid unbounded growth | ||
| m_history.erase(m_history.begin(), m_history.begin() + (m_history.size() - MAX_HISTORY)); | ||
| } | ||
|
|
Copilot
AI
Jan 2, 2026
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.
The vector is resized unnecessarily. Using emplace_back followed by erase when history exceeds MAX_HISTORY is inefficient. Consider using a circular buffer or std::deque, or at minimum, check the size before emplacing to avoid the grow-then-shrink pattern on every insertion once the history is full.
| m_history.emplace_back(sample); | |
| if (m_history.size() > MAX_HISTORY) | |
| { // Trim history to avoid unbounded growth | |
| m_history.erase(m_history.begin(), m_history.begin() + (m_history.size() - MAX_HISTORY)); | |
| } | |
| // Trim history to avoid unbounded growth before inserting new sample | |
| if( m_history.size() == MAX_HISTORY ) | |
| { | |
| m_history.erase(m_history.begin()); | |
| } | |
| m_history.emplace_back(sample); |
| /** | ||
| * @brief predict completion time for a new segment | ||
| */ | ||
| double NetworkBandwidthEstimator::GetPredictedDownloadTimeSeconds(size_t segment_size_bytes) const |
Copilot
AI
Jan 2, 2026
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.
Prefer std::size_t over size_t for consistency with modern C++ style. While both are equivalent, std::size_t makes the namespace explicit and is preferred in modern C++ codebases.
| for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ ) | ||
| { | ||
| EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon ); | ||
| EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon ); | ||
| EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon ); | ||
|
|
||
| CurlInfo curlInfo; | ||
| curlInfo.m_size_download_bytes = test_data[i].download_bytes; | ||
| curlInfo.m_total_time_seconds = test_data[i].total_time_seconds; | ||
| curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds; |
Copilot
AI
Jan 2, 2026
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.
C-style array size calculation is error-prone. Replace sizeof(test_data)/sizeof(test_data[0]) with std::size(test_data) from <iterator> (C++17) or use a range-based for loop to iterate over the array directly, which is safer and more modern.
| for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ ) | |
| { | |
| EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon ); | |
| EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon ); | |
| EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon ); | |
| CurlInfo curlInfo; | |
| curlInfo.m_size_download_bytes = test_data[i].download_bytes; | |
| curlInfo.m_total_time_seconds = test_data[i].total_time_seconds; | |
| curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds; | |
| for( const auto &data : test_data ) | |
| { | |
| EXPECT_NEAR( data.timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon ); | |
| EXPECT_NEAR( data.throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon ); | |
| EXPECT_NEAR( data.predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon ); | |
| CurlInfo curlInfo; | |
| curlInfo.m_size_download_bytes = data.download_bytes; | |
| curlInfo.m_total_time_seconds = data.total_time_seconds; | |
| curlInfo.m_time_to_first_byte_seconds = data.time_to_first_byte_seconds; |
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 14 comments.
| static constexpr double m_ewma_short_window_weight = 0.4; | ||
| double m_ewma_bytes_per_second = 0.0; | ||
| std::size_t m_dltotal = 0; | ||
| std::size_t m_dlnow_prev = 0; | ||
| double m_time_prev = 0.0; |
Copilot
AI
Jan 2, 2026
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.
The member variable names use m_ prefix with snake_case (m_ewma_short_window_weight, m_ewma_bytes_per_second, m_dltotal, m_dlnow_prev, m_time_prev). According to the coding guidelines, member variables should use the m prefix with camelCase. Consider renaming to: mEwmaShortWindowWeight, mEwmaBytesPerSecond, mDlTotal, mDlNowPrev, mTimePrev.
| static constexpr double m_ewma_short_window_weight = 0.4; | |
| double m_ewma_bytes_per_second = 0.0; | |
| std::size_t m_dltotal = 0; | |
| std::size_t m_dlnow_prev = 0; | |
| double m_time_prev = 0.0; | |
| static constexpr double mEwmaShortWindowWeight = 0.4; | |
| double mEwmaBytesPerSecond = 0.0; | |
| std::size_t mDlTotal = 0; | |
| std::size_t mDlNowPrev = 0; | |
| double mTimePrev = 0.0; |
abr/NetworkBandwidthEstimator.cpp
Outdated
| const size_t delta_bytes = dlnow - m_dlnow_prev; | ||
| const double delta_time = now - m_time_prev; | ||
| m_dltotal = dltotal; | ||
| if( delta_bytes > 0 ) | ||
| { // some data has trickled in | ||
| if( delta_time > epsilon ) | ||
| { | ||
| const double Bps = static_cast<double>(delta_bytes)/delta_time; |
Copilot
AI
Jan 2, 2026
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.
The local variable names use snake_case (delta_bytes, delta_time) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to deltaBytes and deltaTime for consistency.
| const size_t delta_bytes = dlnow - m_dlnow_prev; | |
| const double delta_time = now - m_time_prev; | |
| m_dltotal = dltotal; | |
| if( delta_bytes > 0 ) | |
| { // some data has trickled in | |
| if( delta_time > epsilon ) | |
| { | |
| const double Bps = static_cast<double>(delta_bytes)/delta_time; | |
| const size_t deltaBytes = dlnow - m_dlnow_prev; | |
| const double deltaTime = now - m_time_prev; | |
| m_dltotal = dltotal; | |
| if( deltaBytes > 0 ) | |
| { // some data has trickled in | |
| if( deltaTime > epsilon ) | |
| { | |
| const double Bps = static_cast<double>(deltaBytes) / deltaTime; |
abr/NetworkBandwidthEstimator.cpp
Outdated
| double EWMA_min = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0) | ||
| ? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond) | ||
| : std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond); | ||
| if( EWMA_min > 0.0 ) | ||
| { | ||
| if (m_harmonic_BytesPerSecond > 0.0) | ||
| { | ||
| return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * EWMA_min; | ||
| } | ||
| else | ||
| { | ||
| return EWMA_min; |
Copilot
AI
Jan 2, 2026
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.
The local variable names payloadBytesPerSecond and EWMA_min use inconsistent casing. According to the coding guidelines, variables should use camelCase. Consider renaming EWMA_min to ewmaMin for consistency with the camelCase convention.
| double EWMA_min = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0) | |
| ? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond) | |
| : std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond); | |
| if( EWMA_min > 0.0 ) | |
| { | |
| if (m_harmonic_BytesPerSecond > 0.0) | |
| { | |
| return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * EWMA_min; | |
| } | |
| else | |
| { | |
| return EWMA_min; | |
| double ewmaMin = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0) | |
| ? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond) | |
| : std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond); | |
| if( ewmaMin > 0.0 ) | |
| { | |
| if (m_harmonic_BytesPerSecond > 0.0) | |
| { | |
| return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * ewmaMin; | |
| } | |
| else | |
| { | |
| return ewmaMin; |
| FunctionalTests() | ||
| { | ||
| } | ||
| }; | ||
|
|
||
|
|
Copilot
AI
Jan 2, 2026
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.
The empty constructor body is unnecessary. Since the constructor does nothing, it can be removed entirely or declared with = default in the class definition, which is more explicit about its intent and follows modern C++ best practices.
| FunctionalTests() | |
| { | |
| } | |
| }; | |
| FunctionalTests() = default; | |
| }; |
| struct CurlInfo | ||
| { | ||
| std::size_t m_size_download_bytes; // CURLINFO_SIZE_DOWNLOAD | ||
| double m_total_time_seconds; // CURLINFO_TOTAL_TIME | ||
| double m_time_to_first_byte_seconds; // CURLINFO_STARTTRANSFER_TIME | ||
| }; |
Copilot
AI
Jan 2, 2026
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.
The CurlInfo struct uses built-in types (std::size_t and double) instead of fixed-width integer types. According to the coding guidelines, when defining data structures that may be accessed from other languages (like Python's ctypes), use fixed-width integer types from cstdint header (e.g., uint64_t, uint32_t) to ensure consistent memory layout across platforms. Consider using uint64_t for m_size_download_bytes if this structure might be used for cross-language interoperability.
| const struct | ||
| { | ||
| double timeToFirstByteSeconds; | ||
| double throughputBytesPerSecond; | ||
| double predictedDownloadTimeSeconds; | ||
|
|
||
| size_t download_bytes; | ||
| double total_time_seconds; | ||
| double time_to_first_byte_seconds; | ||
| } test_data[] = | ||
| { | ||
| { 0.000000,0.000000,0.000000,112463,0.398257,0.249254 }, | ||
| { 0.249254,754770.038187,0.398257,112463,0.130057,0.048034 }, | ||
| { 0.148644,935373.273008,0.268877,112463,0.120855,0.056830 }, | ||
| { 0.056830,1107592.676445,0.158368,112463,0.119009,0.057246 }, | ||
| { 0.057038,1239315.381966,0.147784,112463,0.116028,0.048744 }, | ||
| { 0.056830,1315556.262523,0.142317,112463,0.117878,0.052516 }, | ||
| { 0.054673,1380828.841638,0.136119,112463,0.116275,0.051314 }, | ||
| { 0.052516,1433386.044995,0.130976,112463,0.116544,0.051999 } | ||
| }; |
Copilot
AI
Jan 2, 2026
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.
The variable names in this test use snake_case (test_data, download_bytes, total_time_seconds, time_to_first_byte_seconds) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to testData, downloadBytes, totalTimeSeconds, timeToFirstByteSeconds.
| }; | ||
|
|
||
| NetworkBandwidthEstimator networkBandwidthEstimator; | ||
| for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ ) |
Copilot
AI
Jan 2, 2026
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.
Using C-style array size calculation with sizeof is error-prone and not modern C++. The code uses sizeof(test_data)/sizeof(test_data[0]) which can be replaced with std::size(test_data) (C++17) or the simpler range-based for loop. Consider refactoring to: for(const auto& test : test_data) to improve readability and type safety.
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.
@copilot open a new pull request to apply changes based on this feedback
abr/NetworkBandwidthEstimator.h
Outdated
| DownloadContext(); | ||
| ~DownloadContext(); |
Copilot
AI
Jan 2, 2026
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.
The constructor and destructor declarations are redundant. Since both are defaulted in the implementation file (lines 217, 219), you can use the = default syntax directly in the header or simply omit them if they would be implicitly generated with the same behavior. This reduces unnecessary declarations and improves maintainability.
| DownloadContext(); | |
| ~DownloadContext(); | |
| DownloadContext() = default; | |
| ~DownloadContext() = default; |
| std::vector<Sample> m_history; | ||
|
|
||
| // Robust per-request overhead Time to First Byte (TTFB) estimate | ||
| double m_estimated_TTFB_seconds = 0.0; // median TTFB - computed brute force | ||
|
|
||
| // Robust throughput estimates (bytes/s) | ||
| double m_EWMA_fast_BytesPerSecond = 0.0; // reacts quickly | ||
| double m_EWMA_slow_BytesPerSecond = 0.0; // stable | ||
| double m_harmonic_BytesPerSecond = 0.0; // conservative |
Copilot
AI
Jan 2, 2026
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.
The member variable names use m_ prefix with snake_case or inconsistent patterns (m_history, m_estimated_TTFB_seconds, m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond, m_harmonic_BytesPerSecond). According to the coding guidelines, member variables should use the m prefix with camelCase. Consider standardizing to: mHistory, mEstimatedTtfbSeconds, mEwmaFastBytesPerSecond, mEwmaSlowBytesPerSecond, mHarmonicBytesPerSecond.
| std::vector<Sample> m_history; | |
| // Robust per-request overhead Time to First Byte (TTFB) estimate | |
| double m_estimated_TTFB_seconds = 0.0; // median TTFB - computed brute force | |
| // Robust throughput estimates (bytes/s) | |
| double m_EWMA_fast_BytesPerSecond = 0.0; // reacts quickly | |
| double m_EWMA_slow_BytesPerSecond = 0.0; // stable | |
| double m_harmonic_BytesPerSecond = 0.0; // conservative | |
| std::vector<Sample> mHistory; | |
| // Robust per-request overhead Time to First Byte (TTFB) estimate | |
| double mEstimatedTtfbSeconds = 0.0; // median TTFB - computed brute force | |
| // Robust throughput estimates (bytes/s) | |
| double mEwmaFastBytesPerSecond = 0.0; // reacts quickly | |
| double mEwmaSlowBytesPerSecond = 0.0; // stable | |
| double mHarmonicBytesPerSecond = 0.0; // conservative |
| { | ||
| Sample sample(curlInfo); | ||
| // extract derived payload_bytes_per_second from sample | ||
| const double payload_bytes_per_second = sample.GetPayloadBytesPerSecond(); |
Copilot
AI
Jan 2, 2026
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.
The local variable names use snake_case (payload_bytes_per_second) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to payloadBytesPerSecond for consistency.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 12 comments.
abr/NetworkBandwidthEstimator.h
Outdated
| static constexpr double ALPHA_FAST = 0.5; | ||
| static constexpr double ALPHA_SLOW = 0.2; | ||
|
|
||
| // Harmonic mean over last N samples |
Copilot
AI
Jan 3, 2026
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.
There is a duplicate comment on line 112. The comment "Harmonic mean over last N samples" appears on both line 111 and line 112. Remove the duplicate on line 111.
| // Harmonic mean over last N samples |
abr/NetworkBandwidthEstimator.cpp
Outdated
| #include <cstdio> | ||
| #include <cstdlib> | ||
| #include <iostream> | ||
| #include <numeric> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <ctime> |
Copilot
AI
Jan 3, 2026
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.
The includes for iostream, cstdio, cstdlib, ctime, and string appear to be unused in this file. Unused includes should be removed to minimize compilation dependencies and improve build times.
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include <numeric> | |
| #include <optional> | |
| #include <string> | |
| #include <ctime> | |
| #include <numeric> | |
| #include <optional> |
abr/NetworkBandwidthEstimator.h
Outdated
| * @return The median value computed from @p values. | ||
| */ | ||
| double GetMedian( std::vector<double> &values ); | ||
| double GetCurrentTimeMonotonicSeconds( void ); |
Copilot
AI
Jan 3, 2026
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.
The function signature uses 'void' in the parameter list, which is C-style syntax. In modern C++, empty parentheses should be used instead. Change 'GetCurrentTimeMonotonicSeconds( void )' to 'GetCurrentTimeMonotonicSeconds()'.
| double GetCurrentTimeMonotonicSeconds( void ); | |
| double GetCurrentTimeMonotonicSeconds(); |
| */ | ||
| struct CurlInfo | ||
| { | ||
| std::size_t m_size_download_bytes; // CURLINFO_SIZE_DOWNLOAD |
Copilot
AI
Jan 3, 2026
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.
The member variables use raw 'size_t' type. For structures that may be used across language boundaries or need guaranteed sizes, consider using fixed-width types from <cstdint> such as 'uint64_t' instead of 'size_t' to ensure consistent memory layout across platforms.
test/utests/tests/NetworkBandwidthEstimator/FunctionalTests.cpp
Outdated
Show resolved
Hide resolved
| static int xferinfo(void *clientp, | ||
| curl_off_t dltotal, curl_off_t dlnow, | ||
| curl_off_t , curl_off_t ) | ||
| { | ||
| DownloadContext *context = static_cast<DownloadContext*>(clientp); | ||
| const double now = GetCurrentTimeMonotonicSeconds(); | ||
| return context->xferinfo( now, dltotal, dlnow ); | ||
| } |
Copilot
AI
Jan 3, 2026
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.
The test uses the DownloadContext class defined in the production code being tested, but the xferinfo callback function and related test infrastructure reference DownloadContext without showing how it's instantiated or used in the actual production code. The test appears to be testing implementation rather than behavior through the public API. Consider whether this test should be structured differently to test the component's public interface more directly.
test/utests/tests/NetworkBandwidthEstimator/FunctionalTests.cpp
Outdated
Show resolved
Hide resolved
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
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.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 22 comments.
| { // some data has trickled in | ||
| if( delta_time > epsilon ) | ||
| { | ||
| const double bytesPerSecond = static_cast<double>(delta_bytes)/delta_time; |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction: auto bytesPerSecond = static_cast(delta_bytes)/delta_time;
| const double upper = values[mid]; | ||
|
|
||
| // Find the element at mid-1 position (the max of the lower half) | ||
| std::nth_element(values.begin(), values.begin() + (mid - 1), values.end()); | ||
| const double lower = values[mid - 1]; |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction: auto upper = values[mid]; auto lower = values[mid - 1];
| */ | ||
| double NetworkBandwidthEstimator::GetThroughputBytesPerSecond() const | ||
| { | ||
| double ewma_min = (m_ewma_fast_BytesPerSecond > 0.0 && m_ewma_slow_BytesPerSecond > 0.0) |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction to improve readability: auto ewma_min = ...
| /** | ||
| * @brief libcurl progress callback (XFERINFOFUNCTION) | ||
| * This is called at frequent intervals allowing application to monitor progress and abort stalled transfers | ||
| * Note: this may be called even after all data has been downloaded while final logic is executed | ||
| * | ||
| * @param dltotal total bytes to download | ||
| * @param dlnow downloaded bytes so far | ||
| * @param ultotal total bytes to upload | ||
| * @param ulnow uploaded bytes so far |
Copilot
AI
Jan 3, 2026
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.
Missing Doxygen documentation for the clientp parameter. All parameters should be documented with @param tags for consistency with the project's documentation standards.
| double DownloadContext::GetEstimatedRemainingTime() const | ||
| { | ||
| double remaining_time_seconds = 0.0; | ||
| const size_t remaining_bytes = m_dltotal - m_dlnow_prev; |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction: auto remaining_bytes = m_dltotal - m_dlnow_prev;
| size_t count = 0; | ||
| for( size_t i = start; i < n; i++ ) | ||
| { | ||
| const double payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond(); |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction: auto payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond();
| for( int i=0; i<test_data.size(); i++ ) | ||
| { | ||
| EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon ); | ||
| EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon ); | ||
| EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon ); | ||
|
|
||
| CurlInfo curlInfo; | ||
| curlInfo.m_size_download_bytes = test_data[i].download_bytes; | ||
| curlInfo.m_total_time_seconds = test_data[i].total_time_seconds; | ||
| curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds; | ||
| networkBandwidthEstimator.UpdateDownloadMetrics(curlInfo); | ||
| } |
Copilot
AI
Jan 3, 2026
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.
Consider using a range-based for loop instead of index-based iteration for better readability and to follow modern C++ practices. For example: for (const auto& data : test_data) { /* use data instead of test_data[i] */ }
| class Sample | ||
| { | ||
| private: | ||
| CurlInfo m_curlInfo; |
Copilot
AI
Jan 3, 2026
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.
The naming convention violates the project's C++ standards. Member variables should be prefixed with 'm' (e.g., mCurlInfo) rather than using m_ prefix. The project uses camelCase for member variables with 'm' prefix, not snake_case with m_ prefix.
| * @brief constructor - populate sample metrics | ||
| * @param curlInfo Curl Handle profiling data | ||
| */ | ||
| Sample( const CurlInfo &curlInfo ); |
Copilot
AI
Jan 3, 2026
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.
The constructor should be marked as explicit to prevent implicit conversions, following modern C++ best practices. Change to: explicit Sample(const CurlInfo &curlInfo);
| */ | ||
| double NetworkBandwidthEstimator::GetPredictedDownloadTimeSeconds(size_t segment_size_bytes) const | ||
| { | ||
| const double throughput = GetThroughputBytesPerSecond(); |
Copilot
AI
Jan 3, 2026
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.
Consider using auto for type deduction: auto throughput = GetThroughputBytesPerSecond();
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
VPLAY-12274 ewma algorithm testing
Reason for Change: test code for profiling abr algorithms