creating workflow for mirror::String compression
All-ASCII String characters are stored in 8-bit blocks
instead of 16-bit. The compression has not taken place, but all
workflow are in the code already (changing kUseStringCompression in
string.h file to TRUE will enable the feature)
Notes: Feature works on interpreter only without optimizing
Test art: m ART_TEST_INTERPRETER=true ART_TEST_OPTIMIZING=false
test-art-host
Also tested with String tests from libcore/:
1. libcore.java.lang.StringTest
2. libcore.java.lang.StringBufferTest
3. libcore.java.lang.StringBuilderTest
4. libcore.java.lang.OldStringTest
5. libcore.java.lang.OldStringBufferTest
Memory improvement is 33% (from 6.03% to 4.03%, total String memory
from all apps per total memory of all apps) measured on Angler
with Hprof tools
Bug: 31040547
Change-Id: I9cc92c265ebf1305fc06b5fc33efd83797660cce
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index d492ba3..8695fe8 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -31,6 +31,9 @@
namespace mirror {
+// String Compression
+static constexpr bool kUseStringCompression = false;
+
// C++ mirror of java.lang.String
class MANAGED String FINAL : public Object {
public:
@@ -54,18 +57,28 @@
return &value_[0];
}
+ uint8_t* GetValueCompressed() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return &value_compressed_[0];
+ }
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
size_t SizeOf() SHARED_REQUIRES(Locks::mutator_lock_);
+ // Taking out the first/uppermost bit because it is not part of actual length value
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
int32_t GetLength() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetLengthFromCount(GetCount<kVerifyFlags>());
+ }
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ int32_t GetCount() SHARED_REQUIRES(Locks::mutator_lock_) {
return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(String, count_));
}
void SetCount(int32_t new_count) SHARED_REQUIRES(Locks::mutator_lock_) {
// Count is invariant so use non-transactional mode. Also disable check as we may run inside
// a transaction.
- DCHECK_LE(0, new_count);
+ DCHECK_LE(0, (new_count & INT32_MAX));
SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count);
}
@@ -82,12 +95,6 @@
String* Intern() SHARED_REQUIRES(Locks::mutator_lock_);
- template <bool kIsInstrumented, typename PreFenceVisitor>
- ALWAYS_INLINE static String* Alloc(Thread* self, int32_t utf16_length,
- gc::AllocatorType allocator_type,
- const PreFenceVisitor& pre_fence_visitor)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
-
template <bool kIsInstrumented>
ALWAYS_INLINE static String* AllocFromByteArray(Thread* self, int32_t byte_length,
Handle<ByteArray> array, int32_t offset,
@@ -107,6 +114,11 @@
gc::AllocatorType allocator_type)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ template <bool kIsInstrumented>
+ ALWAYS_INLINE static String* AllocEmptyString(Thread* self,
+ gc::AllocatorType allocator_type)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
static String* AllocFromStrings(Thread* self, Handle<String> string, Handle<String> string2)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
@@ -149,6 +161,10 @@
int32_t FastIndexOf(int32_t ch, int32_t start) SHARED_REQUIRES(Locks::mutator_lock_);
+ template <typename MemoryType>
+ int32_t FastIndexOf(MemoryType* chars, int32_t ch, int32_t start)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
int32_t CompareTo(String* other) SHARED_REQUIRES(Locks::mutator_lock_);
CharArray* ToCharArray(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
@@ -157,6 +173,28 @@
void GetChars(int32_t start, int32_t end, Handle<CharArray> array, int32_t index)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsCompressed() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return kUseStringCompression && GetCompressionFlagFromCount(GetCount());
+ }
+
+ bool IsValueNull() SHARED_REQUIRES(Locks::mutator_lock_);
+
+ template<typename MemoryType>
+ static bool AllASCII(const MemoryType* const chars, const int length);
+
+ ALWAYS_INLINE static bool GetCompressionFlagFromCount(const int32_t count) {
+ return kUseStringCompression && ((count & (1u << 31)) != 0);
+ }
+
+ ALWAYS_INLINE static int32_t GetLengthFromCount(const int32_t count) {
+ return kUseStringCompression ? (count & INT32_MAX) : count;
+ }
+
+ ALWAYS_INLINE static int32_t GetFlaggedCount(const int32_t count) {
+ return kUseStringCompression ? (count | (1u << 31)) : count;
+ }
+
static Class* GetJavaLangString() SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(!java_lang_String_.IsNull());
return java_lang_String_.Read();
@@ -174,12 +212,24 @@
SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code);
}
+ template <bool kIsInstrumented, typename PreFenceVisitor>
+ ALWAYS_INLINE static String* Alloc(Thread* self, int32_t utf16_length_with_flag,
+ gc::AllocatorType allocator_type,
+ const PreFenceVisitor& pre_fence_visitor)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+ // First bit (uppermost/leftmost) is taken out for Compressed/Uncompressed flag
+ // [0] Uncompressed: string uses 16-bit memory | [1] Compressed: 8-bit memory
int32_t count_;
uint32_t hash_code_;
- uint16_t value_[0];
+ // Compression of all-ASCII into 8-bit memory leads to usage one of these fields
+ union {
+ uint16_t value_[0];
+ uint8_t value_compressed_[0];
+ };
static GcRoot<Class> java_lang_String_;