You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by ad...@apache.org on 2019/11/01 20:59:38 UTC
[kudu] branch master updated: [util] Fix comment of faststring
This is an automated email from the ASF dual-hosted git repository.
adar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new a035869 [util] Fix comment of faststring
a035869 is described below
commit a035869886bb5de37cde7b2bf48bdd3def3123ff
Author: lingbin <li...@gmail.com>
AuthorDate: Thu Oct 31 22:11:26 2019 +0800
[util] Fix comment of faststring
1. Under any case, capacity() is greater than or equal to
the data length. If the current length is shorter than
kInitialCapacity, after call shrink_to_fit(),
capacity() will go down to kInitialCapacity.
2. The caller must be responsible for releasing the pointer
release() returns.
Now In `GrowByAtLeast()`, to_reserve (i.e. `len_ + count`)
is usually larger than we really need. This commit also
calculate an exact value when we try to expand the faststring.
To be clearer, this commit rename `GrowByAtLeast()` to
`GrowToAtLeast()`, and make the newcapacity as its argument.
Note: If the capacity needs to grow, it still expand by
at least 50% each time.
Change-Id: Ie4bd4f31216fdf5cfbf70526f1358824fbda225a
Reviewed-on: http://gerrit.cloudera.org:8080/14604
Reviewed-by: Adar Dembo <ad...@cloudera.com>
Tested-by: Adar Dembo <ad...@cloudera.com>
---
src/kudu/util/faststring-test.cc | 63 +++++++++++++++++++++++++++++++++++++++-
src/kudu/util/faststring.cc | 9 +++---
src/kudu/util/faststring.h | 17 ++++++-----
3 files changed, 76 insertions(+), 13 deletions(-)
diff --git a/src/kudu/util/faststring-test.cc b/src/kudu/util/faststring-test.cc
index 07c5697..79dd6b2 100644
--- a/src/kudu/util/faststring-test.cc
+++ b/src/kudu/util/faststring-test.cc
@@ -18,6 +18,7 @@
#include <algorithm>
#include <cstring>
#include <memory>
+#include <string>
#include <gtest/gtest.h>
@@ -26,6 +27,9 @@
#include "kudu/util/random_util.h"
#include "kudu/util/test_util.h"
+using std::string;
+using std::unique_ptr;
+
namespace kudu {
class FaststringTest : public KuduTest {};
@@ -48,7 +52,7 @@ TEST_F(FaststringTest, TestShrinkToFit_SmallerThanInitialCapacity) {
TEST_F(FaststringTest, TestShrinkToFit_Random) {
Random r(GetRandomSeed32());
int kMaxSize = faststring::kInitialCapacity * 2;
- std::unique_ptr<char[]> random_bytes(new char[kMaxSize]);
+ unique_ptr<char[]> random_bytes(new char[kMaxSize]);
RandomString(random_bytes.get(), kMaxSize, &r);
faststring s;
@@ -62,4 +66,61 @@ TEST_F(FaststringTest, TestShrinkToFit_Random) {
}
}
+TEST_F(FaststringTest, TestPushBack) {
+ faststring s;
+ for (int i = 0; i < faststring::kInitialCapacity * 2; ++i) {
+ s.push_back('a');
+ ASSERT_LE(s.size(), s.capacity());
+ ASSERT_EQ(i + 1, s.size());
+ if (i + 1 <= faststring::kInitialCapacity) {
+ ASSERT_EQ(s.capacity(), faststring::kInitialCapacity);
+ }
+ }
+}
+
+TEST_F(FaststringTest, TestAppend_Simple) {
+ faststring s;
+ ASSERT_EQ(s.capacity(), faststring::kInitialCapacity);
+ ASSERT_EQ(s.size(), 0);
+
+ // append empty string
+ s.append("");
+ ASSERT_EQ(s.capacity(), faststring::kInitialCapacity);
+ ASSERT_EQ(s.size(), 0);
+
+ // len_ < kInitialCapacity
+ s.append("hello");
+ ASSERT_EQ(s.capacity(), faststring::kInitialCapacity);
+ ASSERT_EQ(s.size(), 5);
+
+ // len_ > kInitialCapacity
+ string tmp(faststring::kInitialCapacity, 'a');
+ s.append(tmp);
+ ASSERT_GT(s.capacity(), faststring::kInitialCapacity);
+ ASSERT_EQ(s.size(), 5 + faststring::kInitialCapacity);
+}
+
+TEST_F(FaststringTest, TestAppend_ExponentiallyExpand) {
+ size_t initial_capacity = faststring::kInitialCapacity / 2;
+ string tmp1(initial_capacity, 'a');
+
+ {
+ // Less than 150% after expansion
+ faststring s;
+ string tmp2(faststring::kInitialCapacity - 1, 'a');
+ s.append(tmp1);
+ s.append(tmp2);
+ ASSERT_EQ(s.capacity(), faststring::kInitialCapacity * 3 / 2);
+ }
+
+ {
+ // More than 150% after expansion
+ faststring s;
+ string tmp2(faststring::kInitialCapacity + 1, 'a');
+ s.append(tmp1);
+ s.append(tmp2);
+ ASSERT_GT(s.capacity(), faststring::kInitialCapacity * 3 / 2);
+ }
+}
+
} // namespace kudu
diff --git a/src/kudu/util/faststring.cc b/src/kudu/util/faststring.cc
index a1cd26b..33610d0 100644
--- a/src/kudu/util/faststring.cc
+++ b/src/kudu/util/faststring.cc
@@ -22,17 +22,16 @@
namespace kudu {
-void faststring::GrowByAtLeast(size_t count) {
+void faststring::GrowToAtLeast(size_t newcapacity) {
// Not enough space, need to reserve more.
// Don't reserve exactly enough space for the new string -- that makes it
// too easy to write perf bugs where you get O(n^2) append.
// Instead, alwayhs expand by at least 50%.
- size_t to_reserve = len_ + count;
- if (len_ + count < len_ * 3 / 2) {
- to_reserve = len_ * 3 / 2;
+ if (newcapacity < capacity_ * 3 / 2) {
+ newcapacity = capacity_ * 3 / 2;
}
- GrowArray(to_reserve);
+ GrowArray(newcapacity);
}
void faststring::GrowArray(size_t newcapacity) {
diff --git a/src/kudu/util/faststring.h b/src/kudu/util/faststring.h
index 992060b..3ada37c 100644
--- a/src/kudu/util/faststring.h
+++ b/src/kudu/util/faststring.h
@@ -86,7 +86,8 @@ class faststring {
// Releases the underlying array; after this, the buffer is left empty.
//
- // NOTE: the data pointer returned by release() is not necessarily the pointer
+ // NOTE: the data pointer returned by release() always points to dynamically
+ // allocated memory and the caller must be responsible for releasing it.
uint8_t *release() WARN_UNUSED_RESULT {
uint8_t *ret = data_;
if (ret == initial_data_) {
@@ -208,10 +209,10 @@ class faststring {
// Reallocates the internal storage to fit only the current data.
//
// This may revert to using internal storage if the current length is shorter than
- // kInitialCapacity. Note that, in that case, after this call, capacity() will return
- // a capacity larger than the data length.
+ // kInitialCapacity. In that case, after this call, capacity() will go down to
+ // kInitialCapacity.
//
- // Any pointers within this instance are invalidated.
+ // Any pointers within this instance may be invalidated.
void shrink_to_fit() {
if (data_ == initial_data_ || capacity_ == len_) return;
ShrinkToFitInternal();
@@ -235,12 +236,12 @@ class faststring {
// Call the non-inline slow path - this reduces the number of instructions
// on the hot path.
- GrowByAtLeast(count);
+ GrowToAtLeast(len_ + count);
}
- // The slow path of MakeRoomFor. Grows the buffer by either
+ // The slow path of EnsureRoomForAppend. Grows the buffer by either
// 'count' bytes, or 50%, whichever is more.
- void GrowByAtLeast(size_t count);
+ void GrowToAtLeast(size_t newcapacity);
// Grow the array to the given capacity, which must be more than
// the current capacity.
@@ -251,6 +252,8 @@ class faststring {
uint8_t* data_;
uint8_t initial_data_[kInitialCapacity];
size_t len_;
+ // NOTE: we will make a initial buffer as part of the object, so the smallest
+ // possible value of capacity_ is kInitialCapacity.
size_t capacity_;
};