WEB开发网
开发学院软件开发VC Visual C++优化对大型数据集合的并发访问 阅读

Visual C++优化对大型数据集合的并发访问

 2010-08-22 20:47:32 来源:WEB开发网   
核心提示:当 CD 启动时,它将首先解释其命令行参数,Visual C++优化对大型数据集合的并发访问(6),然后创建数据库(参见图 7),接下来,我可以将输出重定向到具有 .csv 文件扩展名的文件中,并将其直接导入 Microsoft Excel,CD 将打印输出标题,(如果它在您看来不像是合法的 C++

当 CD 启动时,它将首先解释其命令行参数,然后创建数据库(参见图 7)。接下来,CD 将打印输出标题。(如果它在您看来不像是合法的 C++,则您尚未学习有关便利的 C 预处理器功能的内容。该预处理器将所有仅由空格分隔的字符串文字连接在一起。)最后,CD 针对命令行上指定的每个线程数目调用 doTest 两次。第一次测试以最明显的方式同步对数据库的访问。第二次使用我的经过优化的技巧。为了确保得到有用的错误消息,主函数还会捕获最可能的异常类型。

Figure 8 doTest

typedef vector<auto_ptr<WorkerThread> > ThreadList;
typedef ThreadList::const_iterator   ThreadIter;
static void doTest(Database& db, unsigned long numIterations,
  unsigned long numThreads)
{
  // Set up thread pool:
  ThreadList threads;
  threads.reserve(numThreads);
  for (unsigned i = 0; i < numThreads; i++)
  {
    WorkerThread* pThread = new WorkerThread(i, numThreads,
      numIterations, db);
    threads.push_back(auto_ptr<WorkerThread>(pThread));
  }
  Timer timer;
  // Start threads running:
  for (ThreadIter j = threads.begin(); j != threads.end(); j++)
  {
    (*j)->start();
  }
  // Wait on threads:
  for (ThreadIter k = threads.begin(); k != threads.end(); k++)
  {
    (*k)->wait();
  }
  timer.stop();
  cout << ',' << timer.getMilliSec() << flush;
}

图 8

图 8 显示了 doTest 函数。该函数开始时创建了一个线程池,它是指向 WorkerThread 对象的指针的向量。实际上,更准确地说,它是 auto_ptr 对象的向量,以便在销毁该数组时,还将删除所有 WorkerThread 对象。在下一部分中,我将详细讨论 WorkerThread 类。

接下来,doTest 创建一个计时器来对测试进行计时,启动每个线程运行,并且等待各个线程以确保它们全部完成该测试。最后,DoTest 停止计时器并打印结果。请注意,输出格式有一点儿奇怪。它采用逗号分隔值 (CSV) 格式。这样,我可以将输出重定向到具有 .csv 文件扩展名的文件中,并将其直接导入 Microsoft Excel。这可以快速而轻松地创建漂亮的图表。

Figure 9 WorkerThread Class Declaration

class WorkerThread : public Thread
{
public:
  WorkerThread(unsigned long threadNum, unsigned long numThreads,
    unsigned long numIters, Database& db);
  virtual ~WorkerThread();
protected:
  virtual void run();
private:
  enum RequestType { k_read, k_update };
  void getRequest(unsigned long& entryId, RequestType& requestType,
    string& newValue);
  void doUpdate(unsigned long entryId, const string& newValue);
  string doRead(unsigned long entryId);
  static void simulateWork(unsigned long iterations);
  enum
  {
    k_randMax = 1771875ul,
    k_randCoeff = 2416ul,
    k_randOffset = 374441ul
  };
  unsigned long rand(unsigned long hi);
  unsigned long  m_randSeed;
  unsigned long  m_threadNum;
  unsigned long  m_numThreads;
  unsigned long  m_numIters;
  Database&    m_db;
};

Figure 10 WorkerThread Class Implementation

WorkerThread::WorkerThread(unsigned long threadNum,
    unsigned long numThreads, unsigned long numIters, Database& db) :
  m_randSeed(threadNum % k_randMax),
  m_threadNum(threadNum),
  m_numThreads(numThreads),
  m_numIters(numIters),
  m_db(db)
{
}
WorkerThread::~WorkerThread()
{
}
void WorkerThread::run()
{
  // The strange starting point and step size in this loop make sure
  // that there are the correct total number of iterations among all
  // the threads, even if m_numIters is not evenly divisible by
  // m_numThreads.
  for (unsigned long i = m_threadNum; i < m_numIters;
    i += m_numThreads)
  {
    // Get a request:
    unsigned long entryId;
    RequestType requestType;
    string value;
    getRequest(entryId, requestType, value);
    // Process the request:
    if (requestType == k_update)
    {
      doUpdate(entryId, value);
    }
    else
    {
      value = doRead(entryId);
    }
    // In a real-world server, the result would be marshaled back
    // to the client here.
  }
}
void WorkerThread::getRequest(unsigned long& entryId,
  RequestType& requestType, string& newValue)
{
  const unsigned long k_numReadsPerUpdate = 5;
  entryId = rand(m_db.getNumEntries() - 1);
  requestType = (rand(k_numReadsPerUpdate) == 0)
    ? k_update
    : k_read;
  if (requestType == k_update)
  {
    newValue = "new value";
  }
}
void WorkerThread::doUpdate(unsigned long entryId, const string& newValue)
{
  // Update the database:
  DBEntry& entry = m_db.getEntry(entryId);
  LockKeeper<DBEntry> keeper(entry);
  entry.setValue(newValue);
  // Simulate update overhead:
  simulateWork(100);
  // Unlock entry as we return from this method
}
string WorkerThread::doRead(unsigned long entryId)
{
  // Read from the database:
  DBEntry& entry = m_db.getEntry(entryId);
  LockKeeper<DBEntry> keeper(entry);
  string readResult = entry.getValue();
  // Simulate read overhead:
  simulateWork(10);
  return readResult;
  // Unlock entry as we pass out of this scope
}
static double g_dummy = 0; // prevents optimizing simulateWork away
void WorkerThread::simulateWork(unsigned long iterations)
{
  for (unsigned long i = 0; i < iterations; i++)
  {
    // Meaningless work to take up time:
    unsigned long x = i % 37;
    g_dummy = cos(x) + sin(x) + cosh(x) + sinh(x);
  }
}
// This is a very simple pseudo-random number generator, which overcomes
// two limitations of the built-in rand() function:
//  * Its maximum is considerably higher, and
//  * It doesn't serialize calling threads.
// Derived from "Numerical Recipes in C", by Press, Flannery, Teukolsky,
// and Vetterling, ©1988 by Cambridge University Press, pp.209-211.
unsigned long WorkerThread::rand(unsigned long hi)
{
  assert(hi < k_randMax);
  m_randSeed = (m_randSeed * k_randCoeff + k_randOffset) % k_randMax;
  return (hi * m_randSeed) / (k_randMax - 1);
}

上一页  1 2 3 4 5 6 7 8 9  下一页

Tags:Visual 优化 大型

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接