Modernize almost all C-style arrays to std::array - #6398
Conversation
|
clang-format 20 needs to be run on this PR. (execution 19373495928 / attempt 1) |
|
Given that std::array predates Stockfish the original authors and maintainers of the past had ample opportunity to use std:array instead of C arrays if they so chose. However, they haven't and I agree with their decision. C arrays are minimalistic, cleaner, and simpler. This just adds extra lines and unnecessary syntax. I'm very much opposed and consider this an anti simplification. |
|
Agreed with mstembera, while I'm not surprised there's no regression, it just feels messier (especially for multidimensional arrays). And once we take a pointer to it with |
|
I'd suggest a typedef for >1d arrays I think this is a good change |
| std::uint16_t indices[4]; | ||
| std::memcpy(indices, &b, sizeof(b)); | ||
| std::array<std::uint16_t, 4> indices; | ||
| std::memcpy(&indices[0], &b, sizeof(b)); |
There was a problem hiding this comment.
| std::memcpy(&indices[0], &b, sizeof(b)); | |
| std::memcpy(indices.data(), &b, sizeof(b)); |
| MoveList<CAPTURES> ml(pos); | ||
|
|
||
| cur = endBadCaptures = moves; | ||
| cur = endBadCaptures = &moves[0]; |
There was a problem hiding this comment.
| cur = endBadCaptures = &moves[0]; | |
| cur = endBadCaptures = moves.data(); |
There was a problem hiding this comment.
hmm does this make any difference though? I chose this form because it felt clearer to me
There was a problem hiding this comment.
Both versions work but we should keep it consistent and use data() whenever possible (this pull request adds a bunch of data() calls as well).
Some advantages of data():
- It makes it more explicit that we are accessing the raw data.
- It supports some edge cases where
&foo[0]does not work (e.g., https://godbolt.org/z/j7xcz6qer). - It's nicer to read if we have a pointer to a container (e.g., my additional suggestion in
src/nnue/network.cpp). - It prevents us from superfluously calling
&foo[0]on a raw data pointer (i.e.,type* foo) becausefoo.data()won't compile in this case. This happens a few times in this pull request.
|
|
||
| // Prepare the pointers to loop over the bad captures | ||
| cur = moves; | ||
| cur = &moves[0]; |
There was a problem hiding this comment.
| cur = &moves[0]; | |
| cur = moves.data(); |
| MoveList<EVASIONS> ml(pos); | ||
|
|
||
| cur = moves; | ||
| cur = &moves[0]; |
There was a problem hiding this comment.
| cur = &moves[0]; | |
| cur = moves.data(); |
There was a problem hiding this comment.
Could also be improved, while you're at it:
- stream.read(&(*desc)[0], size);
+ stream.read(desc->data(), size);- stream.write(&desc[0], desc.size());
+ stream.write(desc.data(), desc.size());| } | ||
| #else | ||
| std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions); | ||
| std::memcpy(&output[0], &biases[0], sizeof(std::int32_t) * OutputDimensions); |
There was a problem hiding this comment.
| std::memcpy(&output[0], &biases[0], sizeof(std::int32_t) * OutputDimensions); | |
| std::memcpy(output, biases.data(), sizeof(std::int32_t) * OutputDimensions); |
| const auto input32 = reinterpret_cast<const std::int32_t*>(&input[0]); | ||
| const vec_t* biasvec = reinterpret_cast<const vec_t*>(&biases[0]); |
There was a problem hiding this comment.
| const auto input32 = reinterpret_cast<const std::int32_t*>(&input[0]); | |
| const vec_t* biasvec = reinterpret_cast<const vec_t*>(&biases[0]); | |
| const auto input32 = reinterpret_cast<const std::int32_t*>(input); | |
| const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases.data()); |
| } | ||
|
|
||
| vec_t* outptr = reinterpret_cast<vec_t*>(output); | ||
| vec_t* outptr = reinterpret_cast<vec_t*>(&output[0]); |
There was a problem hiding this comment.
| vec_t* outptr = reinterpret_cast<vec_t*>(&output[0]); | |
| vec_t* outptr = reinterpret_cast<vec_t*>(output); |
| IndexType count; | ||
|
|
||
| const auto input32 = reinterpret_cast<const std::int32_t*>(input); | ||
| const auto input32 = reinterpret_cast<const std::int32_t*>(&input[0]); |
There was a problem hiding this comment.
| const auto input32 = reinterpret_cast<const std::int32_t*>(&input[0]); | |
| const auto input32 = reinterpret_cast<const std::int32_t*>(input); |
| const auto in = reinterpret_cast<const __m256i*>(&input[0]); | ||
| const auto out = reinterpret_cast<__m256i*>(&output[0]); |
There was a problem hiding this comment.
| const auto in = reinterpret_cast<const __m256i*>(&input[0]); | |
| const auto out = reinterpret_cast<__m256i*>(&output[0]); | |
| const auto in = reinterpret_cast<const __m256i*>(input); | |
| const auto out = reinterpret_cast<__m256i*>(output); |
|
@mstembera the main reason I made this PR is because C-style arrays have becoming a limiting factor when refactoring threat inputs. While it is true that std::array is available for Glaurung authors, modern C++ has come a long way, and is generally regarded now as a superior alternative. |
|
@anematode There is actually quite a bit of simplifications |
AFIK it's far from a done deal that threat inputs will make it into master (although i am rooting for it). If it makes it we can include whatever arrays actually need the change then. I disagree that std:array is a superior alternative for OUR use case. If it was the change would have happened long ago. |
|
@mstembera You are right that threat inputs is still WIP. My point is that working on threat inputs has revealed, quite clearly, that the current use of C-style arrays are creating unnecessary barriers for extending the code. Even with extensibility aside, |
|
I like this change, as it makes the codebase more consistent (in using modern C++) and safer, without any runtime effect (same assembly). Arguments of minimalism and simplicity of plain C arrays feel subjective and don't outweigh the benefits. As an example, consider the ease at which arrays can be |
Hmm Piece board[SQUARE_NB];
memset(board, 0, sizeof(board));🤔 Arguments for extensibility and safety are being put forth as reasons for this patch. The SF philosophy has always been of simplicity, to add whatever is necessary but not to put stuff in "just in case". In over a decade of participating on the SF project we have not had any safety issues with C-style arrays. I'm not against changing some small subset as part of an eventual threat inputs PR but that's not a justification for a wholesale change now. |
|
@mstembera You are right that my example wasn't the best showcase of |
|
I think it's still important to stress that Piece board[SQUARE_NB];
memset(board, 0, sizeof(board));With std::array<Piece, SQUARE_NB> board;
board.fill(Piece(0));or even std::array<Piece, SQUARE_NB> board{};One could argue that C-style arrays are bearable here, but consider returning an array from a function: Line 212 in 69a01b8 Now unlike Stockfish/src/nnue/nnue_accumulator.cpp Line 416 in 69a01b8 With Stockfish/src/nnue/nnue_accumulator.cpp Line 417 in 27ed8b3 You can find more examples like this in the diff. The simplification is especially desirable for verbose |
Thanks for making this point. I agree here the syntax is much nicer. However, for the vast majority of the lines this patch touches the opposite is true. |
|
I presume you are talking about the use of Moreover, is it debatable whether the use of .data is actually a case of overly complex syntax. |
|
Not just that. If you look at every line changed many more are more complex and verbose compared to how many are simpler. |
|
I very much disagree with generalizing opinions only based on the line count diff. Most of the additional code is extremely localized to extending the MultiArray implementation, which has nothing to do with the main logic. A few lines of code are for extra headers. The vast majority of the diff are neither simpler or more complicated, while in quite a few places of actual program logic (not just the one I explicity demonstrated), the syntax is substantially cleaner. |
Exactly and currently this code doesn't have to exist .
Again currently we don't need these extra headers.
Declarations and definitions are code. If I look at |
|
I'm afraid the matter of this disagreement has moved beyond the scope of this PR. All recent refactors of Stockfish, as seen in #5750 and #5927 are operated on the principle that simplicity is measured by expressiveness, not verbosity. This is why I do not consider the extra code in MultiArray relevant to assessing whether this PR simplifies the code. If you disagree strongly with this, then perhaps opening a discussion is a more appropriate channel. |
0744762 to
316170c
Compare
Passed Non-regression STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 109280 W: 28619 L: 28482 D: 52179
Ptnml(0-2): 309, 12109, 29719, 12142, 361
https://tests.stockfishchess.org/tests/view/69097b22ea4b268f1fac2a45
No functional change