我有一个非常直接的服务器(使用kryonet)。客户只存储汽车的当前状态(x,y,角度等)并发送加速和转弯请求。
服务器正在接收请求并将它们添加到物理线程耗尽,读取和更新的ArrayBlockingQueue中。
当添加另一个玩家时,游戏的速度减慢了几乎两倍。我已经排除了很多东西(我有所有更新和封装发送限制在60Hz。)
我怀疑使用阻塞队列阻塞太多,导致速度减慢。
如何在不阻塞问题的情况下将客户端请求发送到物理线程?
答案 0 :(得分:2)
我怀疑使用阻塞队列阻塞太多,导致速度减慢。
你怀疑是错的。以下测试程序通过ArrayBlockingQueue推送100万个整数:
public class ArrayBlockingQueuePerfTest {
int maxi = 1000000;
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1000,
true);
Thread sender = new Thread("sender") {
public void run() {
try {
for (int i = 0; i < maxi; i++) {
queue.offer(i, 1, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
};
Thread receiver = new Thread("receiver") {
public void run() {
try {
int count = 0;
long sum = 0;
while (count < maxi) {
sum += queue.poll(1, TimeUnit.SECONDS);
count++;
}
System.out.println("done");
System.out.println("expected sum: " + ((long) maxi) * (maxi - 1) / 2);
System.out.println("actual sum: " + sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
};
public ArrayBlockingQueuePerfTest() {
sender.start();
receiver.start();
}
public static void main(String[] args) {
new ArrayBlockingQueuePerfTest();
}
}
在我的笔记本电脑上,它会在几秒钟后终止。因此,无论您的性能瓶颈在哪里,都不是ArrayBlockingQueue,它可以处理比您需要的吞吐量至少高3个数量级的吞吐量。换句话说,即使您发现线程通信方法根本不需要执行时间,也只会使您的程序加速最多0.1%。
为此以及所有其他性能问题上课:解决现有代码中的任何性能问题的第一步是测量代码的哪一部分很慢,因为通常情况下,它不在哪里一个人期待。 Profilers大大简化了这项任务。
答案 1 :(得分:1)
您可以使用disruptor(环形缓冲区),一种用于实现队列的无锁机制。参见:
答案 2 :(得分:1)
我找到了这个bug。我需要以不同的方式限制物理模拟(不是使用world.step()函数,而是限制调用的频率)。就是这样。
while(true)
{
delta = System.nanoTime() - timer;
if(delta >= 16666666) // 60 Hz
{
world.step(1.0f, 6, 2);
processAndUpdateYourData();
timer = System.nanoTime();
}
}
然后我需要调整所有物理编号,以便他们在这种配置下感觉自然。
答案 3 :(得分:0)
你的问题预先假定了一个实现 - 你最好问“为什么我的代码这么慢?”。
阻塞队列是实现生产者/消费者模式的最有效方式。
我会添加更多的消费者线程 - 尝试添加与处理器核心一样多的消费者线程 - 即Runtime.getRuntime().availableProcessors()
。
答案 4 :(得分:0)
确保您创建的队列中有足够的插槽可用于满足所有客户端。如果队列因客户端太多而变满,则在尝试插入命令时会阻塞。如果这不是问题,则意味着您的物理(消费者)线程无法跟上请求,您需要确保它获得更多的处理时间。