Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-138071

Main thread uses 100% of one CPU core when no users are logged in, busy-waiting over java.lang.Thread::yield, makes it hard to profile properly

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Minecraft 19w11a
    • Minecraft 18w43b, Minecraft 18w43c, Minecraft 18w50a, Minecraft 19w02a, Minecraft 19w05a, Minecraft 19w06a, Minecraft 19w07a, Minecraft 19w08a, Minecraft 19w08b, Minecraft 19w09a
    • None
    • Community Consensus

      The 1.14 snapshots have some severe performance problems. Servers fall behind continually, and users are regularly kicked. The first thing I noticed is that even when nobody is logged on to a server, 100% of one CPU sure is being used. As you'll see, this is a red herring, but it means that everyone else is stuck until this is implemented properly.

      I profiled this, and I found that Thread::yield dominates the run time:

      Tree Profile:
       (t 100.0,s  7.9) java.lang.Thread::run
        (t 92.1,s  0.3) net.minecraft.server.MinecraftServer::run
         (t 87.9,s 87.9) java.lang.Thread::yield
         (t  1.9,s  0.0) net.minecraft.server.MinecraftServer::a
      

      I only have obfuscated names, here's where that's called from in MinecraftServer.run():

          @Override
          public void run() {
              try {
                  if (this.d()) {
                      this.Z = k.b();
                      this.n.a(new jf(this.E));
                      this.n.a(new pd.c("18w43c", 442));
                      this.a(this.n);
                      while (this.u) {
                          long l2 = k.b() - this.Z;
                          if (l2 > 2000L && this.Z - this.Q >= 15000L) {
                              long l3 = l2 / 50L;
                              h.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", (Object)l2, (Object)l3);
                              this.Z += l3 * 50L;
                              this.Q = this.Z;
                          }
                          this.Z += 50L;
                          this.a(this::aU);
                          while (this.aU()) {
                              Thread.yield();
                          }
                          this.P = true;
                      }
                  } else {
                      this.a((b)null);
                  }
              }
      

      Specifically:

                          while (this.aU()) {
                              Thread.yield();
                          }
      

      There are multiple other places where Thread::yield is called from, including another place in MinecraftServer and in ty.java.

      This may be decompiled code, but it looks intentional. Basically, it looks like a hack that was necessary to get a snapshot out in reasonable time. No negative judgement there; we all do cheap hacks to get alpha releases usable for others, and we're better off with having a snapshot than not!

      Unfortunately, this makes it hard for anyone else to help with profiling the server, and as a result, we are unable to help the developers find the performance hotspots that their profiling tools miss.

      This problem also makes it inadvisable or disallowed to run 1.14 snapshots in the cloud, as some VM hosts will kill CPU-hogging processes, and it also can cost more.

      Therefore I urge the developers to please implement this properly sooner rather than later.

            Unassigned Unassigned
            theosib2 Timothy Miller
            Votes:
            65 Vote for this issue
            Watchers:
            42 Start watching this issue

              Created:
              Updated:
              Resolved:
              CHK: