public int getSinkDefaultDOP() {
        // load includes query engine execution and storage engine execution
        // so we can't let query engine use up resources
        // At the same time, the improvement of performance and concurrency is not linear
        // but the memory usage increases linearly, so we control the slope of concurrent growth
        int avgCoreNum = getAvgNumHardwareCoresOfBe();
        if (avgCoreNum <= 24) {
            return Math.max(1, avgCoreNum / 3);
        } else {
            return Math.min(32, avgCoreNum / 4);
        }
    }
@Test
    public void getSinkDefaultDOP() {
        BackendResourceStat stat = BackendResourceStat.getInstance();

        stat.setNumHardwareCoresOfBe(0L, 23);
        stat.setNumHardwareCoresOfBe(1L, 23);
        assertThat(stat.getAvgNumHardwareCoresOfBe()).isEqualTo(23);
        assertThat(stat.getSinkDefaultDOP()).isEqualTo(23 / 3);

        stat.reset();

        stat.setNumHardwareCoresOfBe(0L, 32);
        stat.setNumHardwareCoresOfBe(1L, 32);
        assertThat(stat.getAvgNumHardwareCoresOfBe()).isEqualTo(32);
        assertThat(stat.getSinkDefaultDOP()).isEqualTo(32 / 4);

        stat.reset();

        stat.setNumHardwareCoresOfBe(0L, 32 * 2 * 4);
        stat.setNumHardwareCoresOfBe(1L, 32 * 2 * 4);
        assertThat(stat.getAvgNumHardwareCoresOfBe()).isEqualTo(32 * 2 * 4);
        assertThat(stat.getSinkDefaultDOP()).isEqualTo(32);
    }