-
-
Notifications
You must be signed in to change notification settings - Fork 838
ICU-20392 Split the Locale payload into nested and heap allocated #3518
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
e348eb0 to
33362b4
Compare
markusicu
left a comment
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.
Very nice!
Except, I was hoping for sizeof(Locale) to go down even more.
I think with these changes you are getting 48 bytes on a 64-bit machine for
- vtable pointer
- variant payload
- variant discriminator
- union of Nest / unique_ptr, 32B and 8B-aligned for the pointer
This makes the variant discriminator take up 8 bytes (because of the alignment/padding), distinguishing three states (bogus, Nest, unique_ptr).
Idea: Replace the std::variant with an explicit/manual one, using a union for the heap pointer vs. some other fields.
enum Which : uint8_t { BOGUS, NEST, HEAP };
struct Payload {
union {
struct {
char language[4];
char region[4];
} langRegion;
std::unique_ptr<Heap> heapPtr;
} langRegionOrHeap;
Which which : 2;
uint8_t variantBegin : 6; // 6 to fill the byte, 5 would suffice
char script[5];
char baseName[18];
Payload() {
which = BOGUS;
}
~Payload() {
// if which == HEAP: release langRegionOrHeap.heapPtr
}
...
};
That should get us to 8B vtable + 32B payload = 40B.
I think that shaving off another 8B in every Locale instance is worth some localized fiddling with the discriminator & union.
WDYT?
|
For the first step here, I'd very much like to use the well-tested and type-safe standard library The next most important optimization after that would then be to eliminate the storage currently wasted by With that done, these changes will have resulted in significant size reductions for all kinds of At that point the primary problem will have been fully solved and then we can start looking into further improvements. I myself would then like to start out by taking a critical look at |
Ok for a first step / first PR.
I partially disagree. I think that making the Locale object smaller for commonly used locale IDs is most important. CharString is "only" used for less-common locale IDs. sizeof(Heap) should be reduced after sizeof(Locale). I agree that then there are other opportunities, such as making the parser/initBaseName() less convoluted, and even storing keywords in an at least slightly structured way, so that we need not traipse through the string all the time. |
All the most commonly used Locale objects have very little payload, most of them don't use any extensions, don't use a language tag longer than 3 characters and don't use more than a single variant. There's room for all that data in a simple 32 byte large payload object, which can be nested directly in the Locale object. Any payload larger than that can instead be heap allocated as needed, in order to save storage for the most commonly used objects while retaining the ability to create arbitrarily large and complex Locale objects. This reduces the storage requirements for all Locale objects. For nested payloads, this reduction is from 224 bytes to 48 bytes. For payloads that need to be heap allocated, the reduction depends on several factors, but for most cases there's some reduction. There are also cases where this refactoring actually increases the storage used, because CharString allocates more storage than necessary. There are a number of ways in which this could be improved upon, such as optimizing CharString to not allocate more than necessary when copying a string of known length, not allocating any empty CharString objects or possibly replacing CharString with a new class for fixed length strings. The public API remains unchanged but the operations which can lead to U_MEMORY_ALLOCATION_ERROR change.
|
Hooray! The files in the branch are the same across the force-push. 😃 ~ Your Friendly Jira-GitHub PR Checker Bot |
All the most commonly used
Localeobjects have very little payload, most of them don't use any extensions, don't use a language tag longer than 3 characters and don't use more than a single variant.There's room for all that data in a simple 32 byte large payload object, which can be nested directly in the
Localeobject.Any payload larger than that can instead be heap allocated as needed, in order to save storage for the most commonly used objects while retaining the ability to create arbitrarily large and complex
Localeobjects.This reduces the storage requirements for all
Localeobjects.For nested payloads, this reduction is from 224 bytes to 48 bytes.
For payloads that need to be heap allocated, the reduction depends on several factors, but for most cases there's some reduction. There are also cases where this refactoring actually increases the storage used, because
CharStringallocates more storage than necessary. There are a number of ways in which this could be improved upon, such as optimizingCharStringto not allocate more than necessary when copying a string of known length, not allocating any emptyCharStringobjects or possibly replacingCharStringwith a new class for fixed length strings.The public API remains unchanged but the operations which can lead to
U_MEMORY_ALLOCATION_ERRORchange.Checklist
ALLOW_MANY_COMMITS=true