Merge "Separate DicNodePool from DicNodePriorityQueue."

This commit is contained in:
Keisuke Kuroyanagi 2014-04-01 05:36:35 +00:00 committed by Android (Google) Code Review
commit 94c7bd471f
6 changed files with 129 additions and 183 deletions

View file

@ -24,8 +24,7 @@ DicNode::DicNode(const DicNode &dicNode)
mProfiler(dicNode.mProfiler), mProfiler(dicNode.mProfiler),
#endif #endif
mDicNodeProperties(dicNode.mDicNodeProperties), mDicNodeState(dicNode.mDicNodeState), mDicNodeProperties(dicNode.mDicNodeProperties), mDicNodeState(dicNode.mDicNodeState),
mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion), mIsUsed(dicNode.mIsUsed), mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion) {
mReleaseListener(nullptr) {
/* empty */ /* empty */
} }
@ -36,8 +35,6 @@ DicNode &DicNode::operator=(const DicNode &dicNode) {
mDicNodeProperties = dicNode.mDicNodeProperties; mDicNodeProperties = dicNode.mDicNodeProperties;
mDicNodeState = dicNode.mDicNodeState; mDicNodeState = dicNode.mDicNodeState;
mIsCachedForNextSuggestion = dicNode.mIsCachedForNextSuggestion; mIsCachedForNextSuggestion = dicNode.mIsCachedForNextSuggestion;
mIsUsed = dicNode.mIsUsed;
mReleaseListener = dicNode.mReleaseListener;
return *this; return *this;
} }

View file

@ -19,7 +19,6 @@
#include "defines.h" #include "defines.h"
#include "suggest/core/dicnode/dic_node_profiler.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/dic_node_utils.h"
#include "suggest/core/dicnode/internal/dic_node_state.h" #include "suggest/core/dicnode/internal/dic_node_state.h"
#include "suggest/core/dicnode/internal/dic_node_properties.h" #include "suggest/core/dicnode/internal/dic_node_properties.h"
@ -89,8 +88,7 @@ class DicNode {
#if DEBUG_DICT #if DEBUG_DICT
mProfiler(), mProfiler(),
#endif #endif
mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false), mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false) {}
mIsUsed(false), mReleaseListener(nullptr) {}
DicNode(const DicNode &dicNode); DicNode(const DicNode &dicNode);
DicNode &operator=(const DicNode &dicNode); DicNode &operator=(const DicNode &dicNode);
@ -98,7 +96,6 @@ class DicNode {
// Init for copy // Init for copy
void initByCopy(const DicNode *const dicNode) { void initByCopy(const DicNode *const dicNode) {
mIsUsed = true;
mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
mDicNodeProperties.initByCopy(&dicNode->mDicNodeProperties); mDicNodeProperties.initByCopy(&dicNode->mDicNodeProperties);
mDicNodeState.initByCopy(&dicNode->mDicNodeState); mDicNodeState.initByCopy(&dicNode->mDicNodeState);
@ -107,7 +104,6 @@ class DicNode {
// Init for root with prevWordPtNodePos which is used for bigram // Init for root with prevWordPtNodePos which is used for bigram
void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) { void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) {
mIsUsed = true;
mIsCachedForNextSuggestion = false; mIsCachedForNextSuggestion = false;
mDicNodeProperties.init(rootPtNodeArrayPos, prevWordPtNodePos); mDicNodeProperties.init(rootPtNodeArrayPos, prevWordPtNodePos);
mDicNodeState.init(); mDicNodeState.init();
@ -116,7 +112,6 @@ class DicNode {
// Init for root with previous word // Init for root with previous word
void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) { void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) {
mIsUsed = true;
mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
mDicNodeProperties.init(rootPtNodeArrayPos, dicNode->mDicNodeProperties.getPtNodePos()); mDicNodeProperties.init(rootPtNodeArrayPos, dicNode->mDicNodeProperties.getPtNodePos());
mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState, mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState,
@ -125,7 +120,6 @@ class DicNode {
} }
void initAsPassingChild(DicNode *parentDicNode) { void initAsPassingChild(DicNode *parentDicNode) {
mIsUsed = true;
mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion; mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion;
const int parentCodePoint = parentDicNode->getNodeTypedCodePoint(); const int parentCodePoint = parentDicNode->getNodeTypedCodePoint();
mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint); mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint);
@ -137,7 +131,6 @@ class DicNode {
const int childrenPtNodeArrayPos, const int probability, const bool isTerminal, const int childrenPtNodeArrayPos, const int probability, const bool isTerminal,
const bool hasChildren, const bool isBlacklistedOrNotAWord, const bool hasChildren, const bool isBlacklistedOrNotAWord,
const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) {
mIsUsed = true;
uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1); uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1);
mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
const uint16_t newLeavingDepth = static_cast<uint16_t>( const uint16_t newLeavingDepth = static_cast<uint16_t>(
@ -150,17 +143,6 @@ class DicNode {
PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); 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 { bool isRoot() const {
return getNodeCodePointCount() == 0; return getNodeCodePointCount() == 0;
} }
@ -466,10 +448,6 @@ class DicNode {
#endif #endif
} }
void setReleaseListener(DicNodeReleaseListener *releaseListener) {
mReleaseListener = releaseListener;
}
AK_FORCE_INLINE bool compare(const DicNode *right) const { AK_FORCE_INLINE bool compare(const DicNode *right) const {
// Promote exact matches to prevent them from being pruned. // Promote exact matches to prevent them from being pruned.
const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes()); const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes());
@ -507,8 +485,6 @@ class DicNode {
DicNodeState mDicNodeState; DicNodeState mDicNodeState;
// TODO: Remove // TODO: Remove
bool mIsCachedForNextSuggestion; bool mIsCachedForNextSuggestion;
bool mIsUsed;
DicNodeReleaseListener *mReleaseListener;
AK_FORCE_INLINE int getTotalInputIndex() const { AK_FORCE_INLINE int getTotalInputIndex() const {
int index = 0; int index = 0;

View file

@ -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 <deque>
#include <unordered_set>
#include <vector>
#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<int>(mDicNodes.size())
&& capacity == static_cast<int>(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<const DicNode*> 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<DicNode> mDicNodes;
std::deque<DicNode*> mPooledDicNodes;
};
} // namespace latinime
#endif // LATINIME_DIC_NODE_POOL_H

View file

@ -23,38 +23,30 @@
#include "defines.h" #include "defines.h"
#include "suggest/core/dicnode/dic_node.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 { namespace latinime {
class DicNodePriorityQueue : public DicNodeReleaseListener { class DicNodePriorityQueue {
public: public:
AK_FORCE_INLINE explicit DicNodePriorityQueue(const int capacity) AK_FORCE_INLINE explicit DicNodePriorityQueue(const int capacity)
: mCapacity(capacity), mMaxSize(capacity), mDicNodesBuf(), : mMaxSize(capacity), mDicNodesQueue(), mDicNodePool(capacity) {
mUnusedNodeIndices(), mNextUnusedNodeId(0), mDicNodesQueue() { clear();
mDicNodesBuf.resize(mCapacity + 1);
mUnusedNodeIndices.resize(mCapacity + 1);
clearAndResizeToCapacity();
} }
// Non virtual inline destructor -- never inherit this class // Non virtual inline destructor -- never inherit this class
AK_FORCE_INLINE ~DicNodePriorityQueue() {} AK_FORCE_INLINE ~DicNodePriorityQueue() {}
int getSize() const { AK_FORCE_INLINE int getSize() const {
return static_cast<int>(mDicNodesQueue.size()); return static_cast<int>(mDicNodesQueue.size());
} }
int getMaxSize() const { AK_FORCE_INLINE int getMaxSize() const {
return mMaxSize; return mMaxSize;
} }
AK_FORCE_INLINE void setMaxSize(const int maxSize) { AK_FORCE_INLINE void setMaxSize(const int maxSize) {
ASSERT(maxSize <= mCapacity); mMaxSize = maxSize;
mMaxSize = std::min(maxSize, mCapacity);
}
AK_FORCE_INLINE void clearAndResizeToCapacity() {
clearAndResize(mCapacity);
} }
AK_FORCE_INLINE void clear() { AK_FORCE_INLINE void clear() {
@ -62,25 +54,32 @@ class DicNodePriorityQueue : public DicNodeReleaseListener {
} }
AK_FORCE_INLINE void clearAndResize(const int maxSize) { AK_FORCE_INLINE void clearAndResize(const int maxSize) {
ASSERT(maxSize <= mCapacity); mMaxSize = maxSize;
while (!mDicNodesQueue.empty()) { while (!mDicNodesQueue.empty()) {
mDicNodesQueue.pop(); mDicNodesQueue.pop();
} }
setMaxSize(maxSize); mDicNodePool.reset(mMaxSize + 1);
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);
}
mNextUnusedNodeId = 0;
} }
// Copy AK_FORCE_INLINE void copyPush(const DicNode *const dicNode) {
AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode) { DicNode *const pooledDicNode = newDicNode(dicNode);
return copyPush(dicNode, mMaxSize); if (!pooledDicNode) {
return;
}
if (getSize() < mMaxSize) {
mDicNodesQueue.push(pooledDicNode);
return;
}
if (betterThanWorstDicNode(pooledDicNode)) {
mDicNodePool.placeBackInstance(mDicNodesQueue.top());
mDicNodesQueue.pop();
mDicNodesQueue.push(pooledDicNode);
return;
}
mDicNodePool.placeBackInstance(pooledDicNode);
} }
AK_FORCE_INLINE void copyPop(DicNode *dest) { AK_FORCE_INLINE void copyPop(DicNode *const dest) {
if (mDicNodesQueue.empty()) { if (mDicNodesQueue.empty()) {
ASSERT(false); ASSERT(false);
return; return;
@ -89,34 +88,16 @@ class DicNodePriorityQueue : public DicNodeReleaseListener {
if (dest) { if (dest) {
DicNodeUtils::initByCopy(node, dest); DicNodeUtils::initByCopy(node, dest);
} }
node->finalize(); mDicNodePool.placeBackInstance(node);
mDicNodesQueue.pop(); mDicNodesQueue.pop();
} }
void onReleased(const DicNode *dicNode) { AK_FORCE_INLINE void dump() {
const int index = static_cast<int>(dicNode - &mDicNodesBuf[0]); mDicNodePool.dump();
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");
} }
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue); DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue);
static const int NOT_A_NODE_ID = -1;
AK_FORCE_INLINE static bool compareDicNode(const DicNode *const left, AK_FORCE_INLINE static bool compareDicNode(const DicNode *const left,
const DicNode *const right) { const DicNode *const right) {
@ -124,26 +105,15 @@ class DicNodePriorityQueue : public DicNodeReleaseListener {
} }
struct DicNodeComparator { struct DicNodeComparator {
bool operator ()(DicNode *left, DicNode *right) { bool operator ()(const DicNode *left, const DicNode *right) const {
return compareDicNode(left, right); return compareDicNode(left, right);
} }
}; };
typedef std::priority_queue<DicNode *, std::vector<DicNode *>, DicNodeComparator> DicNodesQueue; typedef std::priority_queue<DicNode *, std::vector<DicNode *>, DicNodeComparator> DicNodesQueue;
const int mCapacity;
int mMaxSize; int mMaxSize;
std::vector<DicNode> mDicNodesBuf; // of each element of mDicNodesBuf respectively
std::vector<int> mUnusedNodeIndices;
int mNextUnusedNodeId;
DicNodesQueue mDicNodesQueue; DicNodesQueue mDicNodesQueue;
DicNodePool mDicNodePool;
inline bool isFull(const int maxSize) const {
return getSize() >= maxSize;
}
AK_FORCE_INLINE void pop() {
copyPop(nullptr);
}
AK_FORCE_INLINE bool betterThanWorstDicNode(const DicNode *const dicNode) const { AK_FORCE_INLINE bool betterThanWorstDicNode(const DicNode *const dicNode) const {
DicNode *worstNode = mDicNodesQueue.top(); DicNode *worstNode = mDicNodesQueue.top();
@ -153,61 +123,13 @@ class DicNodePriorityQueue : public DicNodeReleaseListener {
return compareDicNode(dicNode, worstNode); 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<int>(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) { AK_FORCE_INLINE DicNode *newDicNode(const DicNode *const dicNode) {
DicNode *newNode = searchEmptyDicNode(); DicNode *newNode = mDicNodePool.getInstance();
if (newNode) { if (newNode) {
DicNodeUtils::initByCopy(dicNode, newNode); DicNodeUtils::initByCopy(dicNode, newNode);
} }
return newNode; return newNode;
} }
}; };
} // namespace latinime } // namespace latinime
#endif // LATINIME_DIC_NODE_PRIORITY_QUEUE_H #endif // LATINIME_DIC_NODE_PRIORITY_QUEUE_H

View file

@ -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

View file

@ -49,15 +49,14 @@ class DicNodesCache {
AK_FORCE_INLINE void reset(const int nextActiveSize, const int terminalSize) { AK_FORCE_INLINE void reset(const int nextActiveSize, const int terminalSize) {
mInputIndex = 0; mInputIndex = 0;
mLastCachedInputIndex = 0; mLastCachedInputIndex = 0;
// We want to use the max capacity for the current active dic node queue. // The size of current active DicNode queue doesn't have to be changed.
mActiveDicNodes->clearAndResizeToCapacity(); mActiveDicNodes->clear();
// nextActiveSize is used to limit the next iteration's active dic node size. // nextActiveSize is used to limit the next iteration's active DicNode size.
const int nextActiveSizeFittingToTheCapacity = std::min(nextActiveSize, getCacheCapacity()); const int nextActiveSizeFittingToTheCapacity = std::min(nextActiveSize, getCacheCapacity());
mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity); mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity);
mTerminalDicNodes->clearAndResize(terminalSize); mTerminalDicNodes->clearAndResize(terminalSize);
// We want to use the max capacity for the cached dic nodes that will be used for the // The size of cached DicNode queue doesn't have to be changed.
// continuous suggestion. mCachedDicNodesForContinuousSuggestion->clear();
mCachedDicNodesForContinuousSuggestion->clearAndResizeToCapacity();
} }
AK_FORCE_INLINE void continueSearch() { AK_FORCE_INLINE void continueSearch() {
@ -95,8 +94,8 @@ class DicNodesCache {
mActiveDicNodes->copyPush(dicNode); mActiveDicNodes->copyPush(dicNode);
} }
AK_FORCE_INLINE bool copyPushContinue(DicNode *dicNode) { AK_FORCE_INLINE void copyPushContinue(DicNode *dicNode) {
return mCachedDicNodesForContinuousSuggestion->copyPush(dicNode); mCachedDicNodesForContinuousSuggestion->copyPush(dicNode);
} }
AK_FORCE_INLINE void copyPushNextActive(DicNode *dicNode) { AK_FORCE_INLINE void copyPushNextActive(DicNode *dicNode) {