diff --git a/native/jni/src/suggest/core/dicnode/dic_node.cpp b/native/jni/src/suggest/core/dicnode/dic_node.cpp index 73855977e..414dc3b1e 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node.cpp @@ -24,8 +24,7 @@ DicNode::DicNode(const DicNode &dicNode) mProfiler(dicNode.mProfiler), #endif mDicNodeProperties(dicNode.mDicNodeProperties), mDicNodeState(dicNode.mDicNodeState), - mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion), mIsUsed(dicNode.mIsUsed), - mReleaseListener(nullptr) { + mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion) { /* empty */ } @@ -36,8 +35,6 @@ DicNode &DicNode::operator=(const DicNode &dicNode) { mDicNodeProperties = dicNode.mDicNodeProperties; mDicNodeState = dicNode.mDicNodeState; mIsCachedForNextSuggestion = dicNode.mIsCachedForNextSuggestion; - mIsUsed = dicNode.mIsUsed; - mReleaseListener = dicNode.mReleaseListener; return *this; } diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 258aa9ce3..b54c39f75 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -19,7 +19,6 @@ #include "defines.h" #include "suggest/core/dicnode/dic_node_profiler.h" -#include "suggest/core/dicnode/dic_node_release_listener.h" #include "suggest/core/dicnode/dic_node_utils.h" #include "suggest/core/dicnode/internal/dic_node_state.h" #include "suggest/core/dicnode/internal/dic_node_properties.h" @@ -89,8 +88,7 @@ class DicNode { #if DEBUG_DICT mProfiler(), #endif - mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false), - mIsUsed(false), mReleaseListener(nullptr) {} + mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false) {} DicNode(const DicNode &dicNode); DicNode &operator=(const DicNode &dicNode); @@ -98,7 +96,6 @@ class DicNode { // Init for copy void initByCopy(const DicNode *const dicNode) { - mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.initByCopy(&dicNode->mDicNodeProperties); mDicNodeState.initByCopy(&dicNode->mDicNodeState); @@ -107,7 +104,6 @@ class DicNode { // Init for root with prevWordPtNodePos which is used for bigram void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) { - mIsUsed = true; mIsCachedForNextSuggestion = false; mDicNodeProperties.init(rootPtNodeArrayPos, prevWordPtNodePos); mDicNodeState.init(); @@ -116,7 +112,6 @@ class DicNode { // Init for root with previous word void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) { - mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.init(rootPtNodeArrayPos, dicNode->mDicNodeProperties.getPtNodePos()); mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState, @@ -125,7 +120,6 @@ class DicNode { } void initAsPassingChild(DicNode *parentDicNode) { - mIsUsed = true; mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion; const int parentCodePoint = parentDicNode->getNodeTypedCodePoint(); mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint); @@ -137,7 +131,6 @@ class DicNode { const int childrenPtNodeArrayPos, const int probability, const bool isTerminal, const bool hasChildren, const bool isBlacklistedOrNotAWord, const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { - mIsUsed = true; uint16_t newDepth = static_cast(dicNode->getNodeCodePointCount() + 1); mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; const uint16_t newLeavingDepth = static_cast( @@ -150,17 +143,6 @@ class DicNode { PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - AK_FORCE_INLINE void finalize() { - mIsUsed = false; - if (mReleaseListener) { - mReleaseListener->onReleased(this); - } - } - - bool isUsed() const { - return mIsUsed; - } - bool isRoot() const { return getNodeCodePointCount() == 0; } @@ -466,10 +448,6 @@ class DicNode { #endif } - void setReleaseListener(DicNodeReleaseListener *releaseListener) { - mReleaseListener = releaseListener; - } - AK_FORCE_INLINE bool compare(const DicNode *right) const { // Promote exact matches to prevent them from being pruned. const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes()); @@ -507,8 +485,6 @@ class DicNode { DicNodeState mDicNodeState; // TODO: Remove bool mIsCachedForNextSuggestion; - bool mIsUsed; - DicNodeReleaseListener *mReleaseListener; AK_FORCE_INLINE int getTotalInputIndex() const { int index = 0; diff --git a/native/jni/src/suggest/core/dicnode/dic_node_pool.h b/native/jni/src/suggest/core/dicnode/dic_node_pool.h new file mode 100644 index 000000000..a660b744f --- /dev/null +++ b/native/jni/src/suggest/core/dicnode/dic_node_pool.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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. + */ + +#ifndef LATINIME_DIC_NODE_POOL_H +#define LATINIME_DIC_NODE_POOL_H + +#include +#include +#include + +#include "defines.h" +#include "suggest/core/dicnode/dic_node.h" + +namespace latinime { + +class DicNodePool { + public: + explicit DicNodePool(const int capacity) : mDicNodes(), mPooledDicNodes() { + reset(capacity); + } + + void reset(const int capacity) { + if (capacity == static_cast(mDicNodes.size()) + && capacity == static_cast(mPooledDicNodes.size())) { + // No need to reset. + return; + } + mDicNodes.resize(capacity); + mDicNodes.shrink_to_fit(); + mPooledDicNodes.clear(); + for (auto &dicNode : mDicNodes) { + mPooledDicNodes.emplace_back(&dicNode); + } + } + + // Get a DicNode instance from the pool. The instance has to be returned by returnInstance(). + DicNode *getInstance() { + if (mPooledDicNodes.empty()) { + return nullptr; + } + DicNode *const dicNode = mPooledDicNodes.back(); + mPooledDicNodes.pop_back(); + return dicNode; + } + + // Return an instance that has been removed from the pool by getInstance() to the pool. The + // instance must not be used after returning without getInstance(). + void placeBackInstance(DicNode *dicNode) { + mPooledDicNodes.emplace_back(dicNode); + } + + void dump() const { + AKLOGI("\n\n\n\n\n==========================="); + std::unordered_set usedDicNodes; + for (const auto &dicNode : mDicNodes) { + usedDicNodes.insert(&dicNode); + } + for (const auto &dicNodePtr : mPooledDicNodes) { + usedDicNodes.erase(dicNodePtr); + } + for (const auto &usedDicNodePtr : usedDicNodes) { + usedDicNodePtr->dump("DIC_NODE_POOL: "); + } + AKLOGI("===========================\n\n\n\n\n"); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePool); + + std::vector mDicNodes; + std::deque mPooledDicNodes; +}; +} // namespace latinime +#endif // LATINIME_DIC_NODE_POOL_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h index 213b1b968..7b753f2e4 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h @@ -23,38 +23,30 @@ #include "defines.h" #include "suggest/core/dicnode/dic_node.h" -#include "suggest/core/dicnode/dic_node_release_listener.h" +#include "suggest/core/dicnode/dic_node_pool.h" namespace latinime { -class DicNodePriorityQueue : public DicNodeReleaseListener { +class DicNodePriorityQueue { public: AK_FORCE_INLINE explicit DicNodePriorityQueue(const int capacity) - : mCapacity(capacity), mMaxSize(capacity), mDicNodesBuf(), - mUnusedNodeIndices(), mNextUnusedNodeId(0), mDicNodesQueue() { - mDicNodesBuf.resize(mCapacity + 1); - mUnusedNodeIndices.resize(mCapacity + 1); - clearAndResizeToCapacity(); + : mMaxSize(capacity), mDicNodesQueue(), mDicNodePool(capacity) { + clear(); } // Non virtual inline destructor -- never inherit this class AK_FORCE_INLINE ~DicNodePriorityQueue() {} - int getSize() const { + AK_FORCE_INLINE int getSize() const { return static_cast(mDicNodesQueue.size()); } - int getMaxSize() const { + AK_FORCE_INLINE int getMaxSize() const { return mMaxSize; } AK_FORCE_INLINE void setMaxSize(const int maxSize) { - ASSERT(maxSize <= mCapacity); - mMaxSize = std::min(maxSize, mCapacity); - } - - AK_FORCE_INLINE void clearAndResizeToCapacity() { - clearAndResize(mCapacity); + mMaxSize = maxSize; } AK_FORCE_INLINE void clear() { @@ -62,25 +54,32 @@ class DicNodePriorityQueue : public DicNodeReleaseListener { } AK_FORCE_INLINE void clearAndResize(const int maxSize) { - ASSERT(maxSize <= mCapacity); + mMaxSize = maxSize; while (!mDicNodesQueue.empty()) { mDicNodesQueue.pop(); } - setMaxSize(maxSize); - for (int i = 0; i < mCapacity + 1; ++i) { - mDicNodesBuf[i].finalize(); - mDicNodesBuf[i].setReleaseListener(this); - mUnusedNodeIndices[i] = (i == mCapacity) ? NOT_A_NODE_ID : (i + 1); + mDicNodePool.reset(mMaxSize + 1); + } + + AK_FORCE_INLINE void copyPush(const DicNode *const dicNode) { + DicNode *const pooledDicNode = newDicNode(dicNode); + if (!pooledDicNode) { + return; } - mNextUnusedNodeId = 0; + if (getSize() < mMaxSize) { + mDicNodesQueue.push(pooledDicNode); + return; + } + if (betterThanWorstDicNode(pooledDicNode)) { + mDicNodePool.placeBackInstance(mDicNodesQueue.top()); + mDicNodesQueue.pop(); + mDicNodesQueue.push(pooledDicNode); + return; + } + mDicNodePool.placeBackInstance(pooledDicNode); } - // Copy - AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode) { - return copyPush(dicNode, mMaxSize); - } - - AK_FORCE_INLINE void copyPop(DicNode *dest) { + AK_FORCE_INLINE void copyPop(DicNode *const dest) { if (mDicNodesQueue.empty()) { ASSERT(false); return; @@ -89,34 +88,16 @@ class DicNodePriorityQueue : public DicNodeReleaseListener { if (dest) { DicNodeUtils::initByCopy(node, dest); } - node->finalize(); + mDicNodePool.placeBackInstance(node); mDicNodesQueue.pop(); } - void onReleased(const DicNode *dicNode) { - const int index = static_cast(dicNode - &mDicNodesBuf[0]); - if (mUnusedNodeIndices[index] != NOT_A_NODE_ID) { - // it's already released - return; - } - mUnusedNodeIndices[index] = mNextUnusedNodeId; - mNextUnusedNodeId = index; - ASSERT(index >= 0 && index < (mCapacity + 1)); - } - - AK_FORCE_INLINE void dump() const { - AKLOGI("\n\n\n\n\n==========================="); - for (int i = 0; i < mCapacity + 1; ++i) { - if (mDicNodesBuf[i].isUsed()) { - mDicNodesBuf[i].dump("QUEUE: "); - } - } - AKLOGI("===========================\n\n\n\n\n"); + AK_FORCE_INLINE void dump() { + mDicNodePool.dump(); } private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue); - static const int NOT_A_NODE_ID = -1; AK_FORCE_INLINE static bool compareDicNode(const DicNode *const left, const DicNode *const right) { @@ -124,26 +105,15 @@ class DicNodePriorityQueue : public DicNodeReleaseListener { } struct DicNodeComparator { - bool operator ()(DicNode *left, DicNode *right) { + bool operator ()(const DicNode *left, const DicNode *right) const { return compareDicNode(left, right); } }; typedef std::priority_queue, DicNodeComparator> DicNodesQueue; - const int mCapacity; int mMaxSize; - std::vector mDicNodesBuf; // of each element of mDicNodesBuf respectively - std::vector mUnusedNodeIndices; - int mNextUnusedNodeId; DicNodesQueue mDicNodesQueue; - - inline bool isFull(const int maxSize) const { - return getSize() >= maxSize; - } - - AK_FORCE_INLINE void pop() { - copyPop(nullptr); - } + DicNodePool mDicNodePool; AK_FORCE_INLINE bool betterThanWorstDicNode(const DicNode *const dicNode) const { DicNode *worstNode = mDicNodesQueue.top(); @@ -153,61 +123,13 @@ class DicNodePriorityQueue : public DicNodeReleaseListener { return compareDicNode(dicNode, worstNode); } - AK_FORCE_INLINE DicNode *searchEmptyDicNode() { - if (mCapacity == 0) { - return nullptr; - } - if (mNextUnusedNodeId == NOT_A_NODE_ID) { - AKLOGI("No unused node found."); - for (int i = 0; i < mCapacity + 1; ++i) { - AKLOGI("Dump node availability, %d, %d, %d", - i, mDicNodesBuf[i].isUsed(), mUnusedNodeIndices[i]); - } - ASSERT(false); - return nullptr; - } - DicNode *dicNode = &mDicNodesBuf[mNextUnusedNodeId]; - markNodeAsUsed(dicNode); - return dicNode; - } - - AK_FORCE_INLINE void markNodeAsUsed(DicNode *dicNode) { - const int index = static_cast(dicNode - &mDicNodesBuf[0]); - mNextUnusedNodeId = mUnusedNodeIndices[index]; - mUnusedNodeIndices[index] = NOT_A_NODE_ID; - ASSERT(index >= 0 && index < (mCapacity + 1)); - } - - AK_FORCE_INLINE DicNode *pushPoolNodeWithMaxSize(DicNode *dicNode, const int maxSize) { - if (!dicNode) { - return nullptr; - } - if (!isFull(maxSize)) { - mDicNodesQueue.push(dicNode); - return dicNode; - } - if (betterThanWorstDicNode(dicNode)) { - pop(); - mDicNodesQueue.push(dicNode); - return dicNode; - } - dicNode->finalize(); - return nullptr; - } - - // Copy - AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode, const int maxSize) { - return pushPoolNodeWithMaxSize(newDicNode(dicNode), maxSize); - } - AK_FORCE_INLINE DicNode *newDicNode(const DicNode *const dicNode) { - DicNode *newNode = searchEmptyDicNode(); + DicNode *newNode = mDicNodePool.getInstance(); if (newNode) { DicNodeUtils::initByCopy(dicNode, newNode); } return newNode; } - }; } // namespace latinime #endif // LATINIME_DIC_NODE_PRIORITY_QUEUE_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h b/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h deleted file mode 100644 index c3f432951..000000000 --- a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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. - */ - -#ifndef LATINIME_DIC_NODE_RELEASE_LISTENER_H -#define LATINIME_DIC_NODE_RELEASE_LISTENER_H - -#include "defines.h" - -namespace latinime { - -class DicNode; - -class DicNodeReleaseListener { - public: - DicNodeReleaseListener() {} - virtual ~DicNodeReleaseListener() {} - virtual void onReleased(const DicNode *dicNode) = 0; - private: - DISALLOW_COPY_AND_ASSIGN(DicNodeReleaseListener); -}; -} // namespace latinime -#endif // LATINIME_DIC_NODE_RELEASE_LISTENER_H diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h index 6b8dc8c96..089d4467f 100644 --- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h +++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h @@ -49,15 +49,14 @@ class DicNodesCache { AK_FORCE_INLINE void reset(const int nextActiveSize, const int terminalSize) { mInputIndex = 0; mLastCachedInputIndex = 0; - // We want to use the max capacity for the current active dic node queue. - mActiveDicNodes->clearAndResizeToCapacity(); - // nextActiveSize is used to limit the next iteration's active dic node size. + // The size of current active DicNode queue doesn't have to be changed. + mActiveDicNodes->clear(); + // nextActiveSize is used to limit the next iteration's active DicNode size. const int nextActiveSizeFittingToTheCapacity = std::min(nextActiveSize, getCacheCapacity()); mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity); mTerminalDicNodes->clearAndResize(terminalSize); - // We want to use the max capacity for the cached dic nodes that will be used for the - // continuous suggestion. - mCachedDicNodesForContinuousSuggestion->clearAndResizeToCapacity(); + // The size of cached DicNode queue doesn't have to be changed. + mCachedDicNodesForContinuousSuggestion->clear(); } AK_FORCE_INLINE void continueSearch() { @@ -95,8 +94,8 @@ class DicNodesCache { mActiveDicNodes->copyPush(dicNode); } - AK_FORCE_INLINE bool copyPushContinue(DicNode *dicNode) { - return mCachedDicNodesForContinuousSuggestion->copyPush(dicNode); + AK_FORCE_INLINE void copyPushContinue(DicNode *dicNode) { + mCachedDicNodesForContinuousSuggestion->copyPush(dicNode); } AK_FORCE_INLINE void copyPushNextActive(DicNode *dicNode) {