3

我需要在沙盒进程上运行不安全的本机代码,并且需要减少进程切换的瓶颈。两个进程(控制器和沙箱)共享两个自动重置事件和用于通信的映射文件(共享内存)的一致视图。

为了使这篇文章更小,我从示例代码中删除了初始化,但事件由控制器创建,使用 DuplicateHandle 复制,然后在工作之前发送到沙箱进程。

控制器来源:

void inSandbox(HANDLE hNewRequest, HANDLE hAnswer, volatile int *shared) {
  int before = *shared;
  for (int i = 0; i < 100000; ++i) {
    // Notify sandbox of a new request and wait for answer.
    SignalObjectAndWait(hNewRequest, hAnswer, INFINITE, FALSE);
  }
  assert(*shared == before + 100000);
}

void inProcess(volatile int *shared) {
  int before = *shared;
  for (int i = 0; i < 100000; ++i) {
    newRequest(shared);
  }
  assert(*shared == before + 100000);
}

void newRequest(volatile int *shared) {
  // In this test, the request only increments an int.
  (*shared)++;
}

沙盒来源:

void sandboxLoop(HANDLE hNewRequest, HANDLE hAnswer, volatile int *shared) {
  // Wait for the first request from controller.
  assert(WaitForSingleObject(hNewRequest, INFINITE) == WAIT_OBJECT_0);
  for(;;) {
    // Perform request.
    newRequest(shared);
    // Notify controller and wait for next request.
    SignalObjectAndWait(hAnswer, hNewRequest, INFINITE, FALSE);
  }
}

void newRequest(volatile int *shared) {
  // In this test, the request only increments an int.
  (*shared)++;
}

测量:

  • inSandbox()- 550 毫秒,约 350k 上下文切换,42% CPU(25% 内核,17% 用户)。
  • inProcess()- 20ms,~2k 上下文切换,55% CPU(2% 内核,53% 用户)。

该机器是Windows 7 Pro,Core 2 Duo P9700,内存为8gb。

一个有趣的事实是,沙盒解决方案使用了 42% 的 CPU 与 55% 的进程内解决方案。另一个值得注意的事实是沙盒解决方案包含 350k 上下文切换,这比我们可以从源代码中推断出的 200k 上下文切换要多得多。

我需要知道是否有办法减少将控制转移到另一个进程的开销。我已经尝试使用管道而不是事件,而且情况更糟。我还尝试完全不使用任何事件,方法是在每个请求上进行沙盒调用SuspendThread(GetCurrentThread())和控制器调用ResumeThread(hSandboxThread),但性能类似于使用事件。

如果您有使用程序集(例如执行手动上下文切换)或 Windows 驱动程序工具包的解决方案,也请告诉我。我不介意必须安装驱动程序来加快速度。

我听说 Google Native Client 做了类似的事情,但我只找到了这个文档。如果您有更多信息,请告诉我。

4

1 回答 1

4

首先要尝试的是提高等待线程的优先级。这应该会减少无关上下文切换的数量。

或者,由于您使用的是 2 核系统,因此使用自旋锁而不是事件将使您的代码更快,但会以系统性能和功耗为代价:

void inSandbox(volatile int *lock, volatile int *shared) 
{
  int i, before = *shared;
  for (i = 0; i < 100000; ++i) {
    *lock = 1;
    while (*lock != 0) { }
  }
  assert(*shared == before + 100000);
}

void newRequest(volatile int *shared) {
  // In this test, the request only increments an int.
  (*shared)++;
}

void sandboxLoop(volatile int *lock, volatile int * shared)
{
  for(;;) {
    while (*lock != 1) { }
    newRequest(shared);
    *lock = 0;
  }
}

在这种情况下,您可能应该设置线程关联掩码和/或降低旋转线程的优先级,以便它不会与繁忙的线程竞争 CPU 时间。

理想情况下,您会使用混合方法。当一方要忙一段时间时,让另一方等待一个事件,以便其他进程可以获得一些 CPU 时间。您可以提前一点触发事件(使用自旋锁保持同步),以便其他线程在您准备好时准备好。

于 2012-07-09T00:16:33.053 回答