-
Notifications
You must be signed in to change notification settings - Fork 830
SmallVector #1912
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
SmallVector #1912
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| /* | ||
| * Copyright 2019 WebAssembly Community Group participants | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| // | ||
| // A vector of elements, which may be small, and uses a fixed space | ||
| // for those small elements. | ||
| // | ||
|
|
||
| #ifndef wasm_support_small_vector_h | ||
| #define wasm_support_small_vector_h | ||
|
|
||
| #include <array> | ||
| #include <iterator> | ||
| #include <vector> | ||
|
|
||
| namespace wasm { | ||
|
|
||
| template<typename T, size_t N> | ||
| class SmallVector { | ||
| // fixed-space storage | ||
| size_t usedFixed = 0; | ||
| std::array<T, N> fixed; | ||
|
|
||
| // flexible additional storage | ||
| std::vector<T> flexible; | ||
|
|
||
| public: | ||
| SmallVector() {} | ||
|
|
||
| T& operator[](size_t i) { | ||
| if (i < N) { | ||
| return fixed[i]; | ||
| } else { | ||
| return flexible[i - N]; | ||
| } | ||
| } | ||
|
|
||
| T operator[](size_t i) const { | ||
| if (i < N) { | ||
| return fixed[i]; | ||
| } else { | ||
| return flexible[i - N]; | ||
| } | ||
| } | ||
|
|
||
| void push_back(const T& x) { | ||
| if (usedFixed < N) { | ||
| fixed[usedFixed++] = x; | ||
| } else { | ||
| flexible.push_back(x); | ||
| } | ||
| } | ||
|
|
||
| template <typename... ArgTypes> | ||
| void emplace_back(ArgTypes &&... Args) { | ||
| if (usedFixed < N) { | ||
| new(&fixed[usedFixed++]) T(std::forward<ArgTypes>(Args)...); | ||
| } else { | ||
| flexible.emplace_back(std::forward<ArgTypes>(Args)...); | ||
| } | ||
| } | ||
|
|
||
| void pop_back() { | ||
| if (flexible.empty()) { | ||
| assert(usedFixed > 0); | ||
| usedFixed--; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assert on usedFixed == 0? |
||
| } else { | ||
| flexible.pop_back(); | ||
| } | ||
| } | ||
|
|
||
| T& back() { | ||
| if (flexible.empty()) { | ||
| assert(usedFixed > 0); | ||
| return fixed[usedFixed - 1]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assert on usedFixed == 0? |
||
| } else { | ||
| return flexible.back(); | ||
| } | ||
| } | ||
|
|
||
| const T& back() const { | ||
| if (flexible.empty()) { | ||
| assert(usedFixed > 0); | ||
| return fixed[usedFixed - 1]; | ||
| } else { | ||
| return flexible.back(); | ||
| } | ||
| } | ||
|
|
||
| size_t size() const { | ||
| return usedFixed + flexible.size(); | ||
| } | ||
|
|
||
| bool empty() const { | ||
| return size() == 0; | ||
| } | ||
|
|
||
| void clear() { | ||
| usedFixed = 0; | ||
| flexible.clear(); | ||
| } | ||
|
|
||
| bool operator==(const SmallVector<T, N>& other) const { | ||
| if (usedFixed != other.usedFixed) return false; | ||
| for (size_t i = 0; i < usedFixed; i++) { | ||
| if (fixed[i] != other.fixed[i]) return false; | ||
| } | ||
| return flexible == other.flexible; | ||
| } | ||
|
|
||
| bool operator!=(const SmallVector<T, N>& other) const { | ||
| return !(*this == other); | ||
| } | ||
|
|
||
| // iteration | ||
|
|
||
| struct Iterator { | ||
| typedef T value_type; | ||
| typedef long difference_type; | ||
| typedef T& reference; | ||
|
|
||
| const SmallVector<T, N>* parent; | ||
| size_t index; | ||
|
|
||
| Iterator(const SmallVector<T, N>* parent, size_t index) : parent(parent), index(index) {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow random access iterator: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator? e.g. use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, changing. Although I'm not sure it's useful to add all of those until they are tested, so just adding what is currently used. |
||
|
|
||
| bool operator!=(const Iterator& other) const { | ||
| return index != other.index || parent != other.parent; | ||
| } | ||
|
|
||
| void operator++() { | ||
| index++; | ||
| } | ||
|
|
||
| Iterator& operator+=(difference_type off) { | ||
| index += off; | ||
| return *this; | ||
| } | ||
|
|
||
| const Iterator operator+(difference_type off) const { | ||
| return Iterator(*this) += off; | ||
| } | ||
|
|
||
| const value_type operator*() const { | ||
| return (*parent)[index]; | ||
| } | ||
| }; | ||
|
|
||
| Iterator begin() const { | ||
| return Iterator(static_cast<const SmallVector<T, N>*>(this), 0); | ||
| } | ||
| Iterator end() const { | ||
| return Iterator(static_cast<const SmallVector<T, N>*>(this), size()); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace wasm | ||
|
|
||
| #endif // wasm_support_small_vector_h | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| #define wasm_wasm_traversal_h | ||
|
|
||
| #include "wasm.h" | ||
| #include "support/small_vector.h" | ||
| #include "support/threads.h" | ||
|
|
||
| namespace wasm { | ||
|
|
@@ -408,6 +409,7 @@ struct Walker : public VisitorType { | |
| struct Task { | ||
| TaskFunc func; | ||
| Expression** currp; | ||
| Task() {} | ||
| Task(TaskFunc func, Expression** currp) : func(func), currp(currp) {} | ||
| }; | ||
|
|
||
|
|
@@ -488,7 +490,7 @@ struct Walker : public VisitorType { | |
|
|
||
| private: | ||
| Expression** replacep = nullptr; // the address of the current node, used to replace it | ||
| std::vector<Task> stack; // stack of tasks | ||
| SmallVector<Task, 10> stack; // stack of tasks | ||
| Function* currFunction = nullptr; // current function being processed | ||
| Module* currModule = nullptr; // current module being processed | ||
| }; | ||
|
|
@@ -716,13 +718,17 @@ struct PostWalker : public Walker<SubType, VisitorType> { | |
| } | ||
| }; | ||
|
|
||
| // Stacks of expressions tend to be limited in size (although, sometimes | ||
| // super-nested blocks exist for br_table). | ||
| typedef SmallVector<Expression*, 10> ExpressionStack; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why 10? I'd think 4 or 8 would be more cache-friendly, though I guess it depends on how big
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 10 pointers on 64-bit would be a multiple of 16, so seems like it should be ok cache-wise, or am I missing something? I kind of picked 10 because I didn't see a benchmark advantage to smaller or larger values, and it seems reasonable as a guess for a "typical" wasm function body. |
||
|
|
||
| // Traversal with a control-flow stack. | ||
|
|
||
| template<typename SubType, typename VisitorType = Visitor<SubType>> | ||
| struct ControlFlowWalker : public PostWalker<SubType, VisitorType> { | ||
| ControlFlowWalker() = default; | ||
|
|
||
| std::vector<Expression*> controlFlowStack; // contains blocks, loops, and ifs | ||
| ExpressionStack controlFlowStack; // contains blocks, loops, and ifs | ||
|
|
||
| // Uses the control flow stack to find the target of a break to a name | ||
| Expression* findBreakTarget(Name name) { | ||
|
|
@@ -785,7 +791,7 @@ template<typename SubType, typename VisitorType = Visitor<SubType>> | |
| struct ExpressionStackWalker : public PostWalker<SubType, VisitorType> { | ||
| ExpressionStackWalker() = default; | ||
|
|
||
| std::vector<Expression*> expressionStack; | ||
| ExpressionStack expressionStack; | ||
|
|
||
| // Uses the control flow stack to find the target of a break to a name | ||
| Expression* findBreakTarget(Name name) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| #include <iostream> | ||
| #include <cassert> | ||
|
|
||
| #include "support/small_vector.h" | ||
|
|
||
| using namespace wasm; | ||
|
|
||
| template<typename T> | ||
| void test() { | ||
| { | ||
| T t; | ||
| // build up | ||
| assert(t.empty()); | ||
| assert(t.size() == 0); | ||
| t.push_back(1); | ||
| assert(!t.empty()); | ||
| assert(t.size() == 1); | ||
| t.push_back(2); | ||
| assert(!t.empty()); | ||
| assert(t.size() == 2); | ||
| t.push_back(3); | ||
| assert(!t.empty()); | ||
| // unwind | ||
| assert(t.size() == 3); | ||
| assert(t.back() == 3); | ||
| t.pop_back(); | ||
| assert(t.size() == 2); | ||
| assert(t.back() == 2); | ||
| t.pop_back(); | ||
| assert(t.size() == 1); | ||
| assert(t.back() == 1); | ||
| t.pop_back(); | ||
| assert(t.size() == 0); | ||
| assert(t.empty()); | ||
| } | ||
| { | ||
| T t; | ||
| // build up | ||
| t.push_back(1); | ||
| t.push_back(2); | ||
| t.push_back(3); | ||
| // unwind | ||
| t.clear(); | ||
| assert(t.size() == 0); | ||
| assert(t.empty()); | ||
| } | ||
| { | ||
| T t, u; | ||
| assert(t == u); | ||
| t.push_back(1); | ||
| assert(t != u); | ||
| u.push_back(1); | ||
| assert(t == u); | ||
| u.pop_back(); | ||
| assert(t != u); | ||
| u.push_back(2); | ||
| assert(t != u); | ||
| } | ||
| } | ||
|
|
||
| int main() { | ||
| test<SmallVector<int, 0>>(); | ||
| test<SmallVector<int, 1>>(); | ||
| test<SmallVector<int, 2>>(); | ||
| test<SmallVector<int, 10>>(); | ||
| std::cout << "ok.\n"; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ok. |
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.
make these private?