Skip to content
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

[READY] Use struct of arrays for static code_points object #1140

Merged
merged 1 commit into from
Dec 8, 2018

Conversation

bstaletic
Copy link
Collaborator

@bstaletic bstaletic commented Nov 30, 2018

Introduce RawCodePointArray for FindCodePoint
to iterate of a continous array instead of
an array of structs.

This also allows the arrays in RawCodePointArray
to use char[] instead of const char* and thus
avoids 12MB of relocation data on linux.

This is another attempt of doing #1064.

Table summary from Michel's comment

Platform Compilation time (s) Library size (MB)
Before After Before After
Ubuntu 18.04 64-bit (GCC 7.3.0) 26.56 25.79 19.59 7.32
macOS 10.14 (Apple Clang 10.0.0) 29.16 28.20 7.09 7.30
Windows 7 64-bit (MSVC 15) 30.62 30.39 7.86 6.99

This change is Reviewable

@codecov
Copy link

codecov bot commented Nov 30, 2018

Codecov Report

Merging #1140 into master will increase coverage by <.01%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master    #1140      +/-   ##
==========================================
+ Coverage   97.69%   97.69%   +<.01%     
==========================================
  Files          90       90              
  Lines        7058     7069      +11     
==========================================
+ Hits         6895     6906      +11     
  Misses        163      163

Copy link
Collaborator

@micbou micbou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. To be thorough, I compared the time it takes to compile the library and the resulting size with and without the proposed changes on platforms we support:

Platform Compilation time (s) Library size (MB)
Before After Before After
Ubuntu 18.04 64-bit (GCC 7.3.0) 26.56 25.79 19.59 7.32
macOS 10.14 (Apple Clang 10.0.0) 29.16 28.20 7.09 7.30
Windows 7 64-bit (MSVC 15) 30.62 30.39 7.86 6.99

The results were obtained by running time ./build.py --no-regex. There is a small decrease in compilation time for all platforms, a huge size reduction on Linux (as expected), a non negligible decrease on Windows, and a minor increase on macOS.

:lgtm_strong:

Reviewed 2 of 3 files at r1, 1 of 1 files at r2, 1 of 1 files at r3.
Reviewable status: 1 of 2 LGTMs obtained

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 1 of 2 LGTMs obtained


update_unicode.py, line 51 at r3 (raw file):

std::array< char[{folded_case_size}], {size} > folded_case;
std::array< char[{swapped_case_size}], {size} > swapped_case;
std::array< bool, {size} > is_letter;

I wonder if we should really use a bitset here. A std::array of bools is a large overhead of unused bits. For an array of 10,000 bools:

bash-3.2$ ./array 
Array: Size: 10000
Bitset: Size: 1256

Possible early optimisation, but this would also have better cache coherency as well as saved padding.

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 1 of 2 LGTMs obtained


update_unicode.py, line 501 at r3 (raw file):

def CppLength( utf8_code_point ):

I think this could benefit from a comment explaining what it does. It would seem to me that the len( bytes() ) representation is canonical and splitting based on some indicator seems... dodgy ?


update_unicode.py, line 512 at r3 (raw file):

  size = len( code_points )
  original_table = '{{'
  original_size = 0

This section looks dense and liable to errors when changing. Could we present it as generalised data, then run an algorithm on it. Something like

table = {
  'original': { 'output': '{{', 'size': 0, 'converter': CppChar },
 'normal': { ... },
 'is_letter': { 'output': '{{', 'converter': CppBool }
}

for t, d in iteritems( table ):
  entry = code_point[ t ]
  d[ 'output' ] += d[ 'converter' ]( entry ) + ','
  d[ 'size' ] += max( CppLength( entry ), d[ 'size' ] )

Does that a) work, b) seem easier to maintain ?

I think it would be easier to review, as this is currently quite repetitive, which can lead to difficulty in spotting errors.

Introduce RawCodePointArray for FindCodePoint
to iterate of a continous array instead of
an array of structs.

This also allows the arrays in RawCodePointArray
to use char[] instead of const char* and thus
avoids 12MB of relocation data on linux.
Copy link
Collaborator Author

@bstaletic bstaletic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 1 of 2 LGTMs obtained


update_unicode.py, line 51 at r3 (raw file):

Previously, puremourning (Ben Jackson) wrote…

I wonder if we should really use a bitset here. A std::array of bools is a large overhead of unused bits. For an array of 10,000 bools:

bash-3.2$ ./array 
Array: Size: 10000
Bitset: Size: 1256

Possible early optimisation, but this would also have better cache coherency as well as saved padding.

It's not 10.000, it's 132.624.

Yes, I used . to separate thousands, sue me!

I'm trying it out.


update_unicode.py, line 501 at r3 (raw file):

Previously, puremourning (Ben Jackson) wrote…

I think this could benefit from a comment explaining what it does. It would seem to me that the len( bytes() ) representation is canonical and splitting based on some indicator seems... dodgy ?

Done, though wording might not be the best. Tell me if you want it reworded.


update_unicode.py, line 512 at r3 (raw file):

Previously, puremourning (Ben Jackson) wrote…

This section looks dense and liable to errors when changing. Could we present it as generalised data, then run an algorithm on it. Something like

table = {
  'original': { 'output': '{{', 'size': 0, 'converter': CppChar },
 'normal': { ... },
 'is_letter': { 'output': '{{', 'converter': CppBool }
}

for t, d in iteritems( table ):
  entry = code_point[ t ]
  d[ 'output' ] += d[ 'converter' ]( entry ) + ','
  d[ 'size' ] += max( CppLength( entry ), d[ 'size' ] )

Does that a) work, b) seem easier to maintain ?

I think it would be easier to review, as this is currently quite repetitive, which can lead to difficulty in spotting errors.

Yes, it works and it is much nicer to look at.
I've used table.items() for two reasons: a) I didn't want to pull in python-future dependency, b) the script was already python3 only.
On the other hand, I think, this makes the script take a little longer to execute.

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 1 of 2 LGTMs obtained


update_unicode.py, line 538 at r4 (raw file):

    d[ 'output' ] = d[ 'output' ].rstrip( ',' ) + '}},'
    if t == 'combining_class':
      d[ 'output' ] = d[ 'output' ].rstrip( ',' )

will this ever do anything, as you previously added '}}' to the end ?

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm: with minor comment

Reviewed 1 of 3 files at r1, 1 of 1 files at r3, 1 of 1 files at r4.
Reviewable status: :shipit: complete! 2 of 2 LGTMs obtained

Copy link
Collaborator Author

@bstaletic bstaletic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: :shipit: complete! 2 of 2 LGTMs obtained


update_unicode.py, line 51 at r3 (raw file):

Previously, bstaletic (Boris Staletic) wrote…

It's not 10.000, it's 132.624.

Yes, I used . to separate thousands, sue me!

I'm trying it out.

I could get std::bitset to compile but it broke tests. I vote for leaving this as is.


update_unicode.py, line 538 at r4 (raw file):

Previously, puremourning (Ben Jackson) wrote…

will this ever do anything, as you previously added '}}' to the end ?

I added }}, because the arrays in the initializer list need to be comma separated. Then on this line I remove the trailing comma, because the last array isn't followed by anything and can't have a trailing comma.

In other words, if you remove this line it won't generate valid C++.

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: :shipit: complete! 2 of 2 LGTMs obtained


update_unicode.py, line 538 at r4 (raw file):

Previously, bstaletic (Boris Staletic) wrote…

I added }}, because the arrays in the initializer list need to be comma separated. Then on this line I remove the trailing comma, because the last array isn't followed by anything and can't have a trailing comma.

In other words, if you remove this line it won't generate valid C++.

erm,... go home ben, you're blind.

carry on.

Copy link
Member

@puremourning puremourning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewable status: :shipit: complete! 2 of 2 LGTMs obtained

@bstaletic
Copy link
Collaborator Author

@zzbot r=micbou

@zzbot
Copy link
Contributor

zzbot commented Dec 7, 2018

📌 Commit 046f37f has been approved by micbou

@zzbot
Copy link
Contributor

zzbot commented Dec 7, 2018

⌛ Testing commit 046f37f with merge 60c5933...

zzbot added a commit that referenced this pull request Dec 7, 2018
[READY] Use struct of arrays for static code_points object

Introduce RawCodePointArray for FindCodePoint
to iterate of a continous array instead of
an array of structs.

This also allows the arrays in RawCodePointArray
to use char[] instead of const char* and thus
avoids 12MB of relocation data on linux.

This is another attempt of doing #1064.

Table summary from [Michel's comment](#1140 (review))

<table>
  <tr>
    <th rowspan="2">Platform</th>
    <th colspan="2">Compilation time (s)</th>
    <th colspan="2">Library size (MB)</th>
  </tr>
  <tr>
    <td>Before</td>
    <td>After</td>
    <td>Before</td>
    <td>After</td>
  </tr>
  <tr>
    <td>Ubuntu 18.04 64-bit (GCC 7.3.0)</td>
    <td>26.56</td>
    <td>25.79</td>
    <td>19.59</td>
    <td>7.32</td>
  </tr>
  <tr>
    <td>macOS 10.14 (Apple Clang 10.0.0)</td>
    <td>29.16</td>
    <td>28.20</td>
    <td>7.09</td>
    <td>7.30</td>
  </tr>
  <tr>
    <td>Windows 7 64-bit (MSVC 15)</td>
    <td>30.62</td>
    <td>30.39</td>
    <td>7.86</td>
    <td>6.99</td>
  </tr>
</table>

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/ycmd/1140)
<!-- Reviewable:end -->
@zzbot
Copy link
Contributor

zzbot commented Dec 8, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: micbou
Pushing 60c5933 to master...

@zzbot zzbot merged commit 046f37f into ycm-core:master Dec 8, 2018
@micbou
Copy link
Collaborator

micbou commented Dec 8, 2018


update_unicode.py, line 512 at r3 (raw file):

Previously, bstaletic (Boris Staletic) wrote…

Yes, it works and it is much nicer to look at.
I've used table.items() for two reasons: a) I didn't want to pull in python-future dependency, b) the script was already python3 only.
On the other hand, I think, this makes the script take a little longer to execute.

A little? It takes now 3 minutes and 23 seconds instead of 12 seconds for me; 17 times longer. I don't think this was worth the change.

@bstaletic
Copy link
Collaborator Author

I didn't measure, but I do remember that I've sat through the script before. Now I do switch to my browser and I know it takes more than 2 minutes. Should we revert the changes in the script?
On the other hand, we run that script very rarely.
@puremourning What are your thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants