static void statsPrint(struct statistic *statList)
{
struct statistic *statEl;
boolean firstHeader = TRUE;
double fixedMeasure = 1.0;

statsHeader();
for (statEl = statList; statEl != NULL; statEl = statEl->next)
    {
    if (firstHeader)
	{
	printf("%5d%10d%8d%8d%10d%9d%8d%6d\n",
	    statEl->chromCount, statEl->boundingElementCount,
	    statEl->totalGaps, statEl->sizeZeroGapCount, statEl->maxGap,
	    statEl->minGap, statEl->medianGap, statEl->meanGap);
	}
    if (statEl->placedItemCount)
	{
	double shoulderPercent = 100.0 * (double)statEl->placedWithinShoulder /
		(double)statEl->placedItemCount;

	if (firstHeader)
	    {
	    printf("Nearest neighbor statistics:\n");
	    printf("    # of   Maximum  Median    Mean # within "
		"%% within # within    ratio  shoulder\n");
	    printf("   items   nearest nearest nearest   %d bp    "
		"%dbp  zero bp  fixed/ran  window\n", shoulder, shoulder);
	    fixedMeasure =  shoulderPercent;
	    }
	printf("%8d%10d%8d%8d%9d %%%7.2f%9d%7.2f%8d\n", statEl->placedItemCount,
	    statEl->maximumNearestNeighbor, statEl->medianNearestNeighbor,
	    statEl->meanNearestNeighbor, statEl->placedWithinShoulder,
	    shoulderPercent,
	    statEl->zeroNeighbor, fixedMeasure/shoulderPercent, shoulder);
	}
    firstHeader = FALSE;
    }
}
btInt TEST_MEM_PERF::test()
{
    assert(initMem(vm["enable-warmup"].as<bool>(),
                   vm["wrline-m"].as<bool>()));

    t_test_config config;
    memset(&config, 0, sizeof(config));

    // What's the AFU frequency (MHz)?
    uint64_t afu_mhz = readCommonCSR(CSR_COMMON_FREQ);

    config.cycles = uint64_t(vm["ts"].as<int>()) * afu_mhz * 1000 * 1000;
    if (config.cycles == 0)
    {
        // Didn't specify --ts.  Use cycles instead.
        config.cycles = uint64_t(vm["tc"].as<int>());
    }

    const uint64_t counter_bits = 40;
    if (config.cycles & (int64_t(-1) << counter_bits))
    {
        cerr << "Run length overflows " << counter_bits << " bit counter" << endl;
        exit(1);
    }

    config.vc = uint8_t(vm["vc"].as<int>());
    assert(config.vc < 4);

    config.rdline_s = vm["rdline-s"].as<bool>();
    config.wrline_m = vm["wrline-m"].as<bool>();

    config.mcl = uint64_t(vm["mcl"].as<int>());
    if ((config.mcl > 4) || (config.mcl == 3))
    {
        cerr << "Illegal multi-line (mcl) parameter:  " << config.mcl << endl;
        exit(1);
    }
    // Encode mcl as 3 bits.  The low 2 are the Verilog t_ccip_clLen and the
    // high bit indicates random sizes.
    config.mcl = (config.mcl - 1) & 7;

    if (vm["test-mode"].as<bool>())
    {
        cout << "# Mem Bytes, Stride, " << statsHeader() << endl;
        t_test_stats stats;

        config.buf_lines = 256;
        config.stride = 12;
        config.enable_writes = true;
        config.enable_reads = true;
        assert(runTest(&config, &stats) == 0);

        cout << 0x100 * CL(1) << " "
             << config.stride << " "
             << stats
             << endl;

        return 0;
    }

    uint64_t stride_incr = 1 + config.mcl;

    uint64_t min_stride = uint64_t(vm["min-stride"].as<int>());
    min_stride = (min_stride + stride_incr - 1) & ~ (stride_incr - 1);
    uint64_t max_stride = uint64_t(vm["max-stride"].as<int>()) + 1;

    bool vcmap_all = vm["vcmap-all"].as<bool>();
    bool vcmap_enable = vm["vcmap-enable"].as<bool>();
    bool vcmap_dynamic = vm["vcmap-dynamic"].as<bool>();
    int32_t vcmap_fixed_vl0_ratio = int32_t(vm["vcmap-fixed"].as<int>());
    cout << "# MCL = " << (config.mcl + 1) << endl
         << "# Cycles per test = " << config.cycles << endl
         << "# AFU MHz = " << afu_mhz << endl
         << "# VC = " << vcNumToName(config.vc) << endl
         << "# VC Map enabled: " << (vcmap_enable ? "true" : "false") << endl;
    if (vcmap_enable)
    {
        cout << "# VC Map all: " << (vcmap_all ? "true" : "false") << endl
             << "# VC Map dynamic: " << (vcmap_dynamic ? "true" : "false") << endl;
        if (! vcmap_dynamic)
        {
            cout << "# VC Map fixed VL0 ratio: " << vcmap_fixed_vl0_ratio << " / 64" << endl;
        }
    }

    // Read
    cout << "#" << endl
         << "# Reads " << (config.rdline_s ? "" : "not ") << "cached" << endl
         << "# Mem Bytes, Stride, " << statsHeader()
         << endl;

    for (uint64_t mem_lines = stride_incr; mem_lines * CL(1) <= buffer_bytes; mem_lines <<= 1)
    {
        config.buf_lines = mem_lines;

        // Vary stride
        uint64_t stride_limit = (mem_lines < max_stride ? mem_lines+1 : max_stride);
        for (uint64_t stride = min_stride; stride < stride_limit; stride += stride_incr)
        {
            t_test_stats stats;

            config.stride = stride;
            config.enable_writes = false;
            config.enable_reads = true;
            assert(runTest(&config, &stats) == 0);

            cout << mem_lines * CL(1) << " "
                 << stride << " "
                 << stats
                 << endl;
        }
    }

    // Write
    cout << endl
         << endl
         << "# Writes " << (config.wrline_m ? "" : "not ") << "cached" << endl
         << "# Mem Bytes, Stride, " << statsHeader()
         << endl;

    for (uint64_t mem_lines = stride_incr; mem_lines * CL(1) <= buffer_bytes; mem_lines <<= 1)
    {
        config.buf_lines = mem_lines;

        // Vary stride
        uint64_t stride_limit = (mem_lines < max_stride ? mem_lines+1 : max_stride);
        for (uint64_t stride = min_stride; stride < stride_limit; stride += stride_incr)
        {
            t_test_stats stats;

            config.stride = stride;
            config.enable_writes = true;
            config.enable_reads = false;
            assert(runTest(&config, &stats) == 0);

            cout << mem_lines * CL(1) << " "
                 << stride << " "
                 << stats
                 << endl;
        }
    }

    // Throughput (independent read and write)
    cout << endl
         << endl
         << "# Reads " << (config.rdline_s ? "" : "not ") << "cached +"
         << " Writes " << (config.wrline_m ? "" : "not ") << "cached" << endl
         << "# Mem Bytes, Stride, " << statsHeader()
         << endl;

    for (uint64_t mem_lines = stride_incr; mem_lines * CL(1) <= buffer_bytes; mem_lines <<= 1)
    {
        config.buf_lines = mem_lines;

        // Vary stride
        uint64_t stride_limit = (mem_lines < max_stride ? mem_lines+1 : max_stride);
        for (uint64_t stride = min_stride; stride < stride_limit; stride += stride_incr)
        {
            t_test_stats stats;

            config.stride = stride;
            config.enable_writes = true;
            config.enable_reads = true;
            assert(runTest(&config, &stats) == 0);

            cout << mem_lines * CL(1) << " "
                 << stride << " "
                 << stats
                 << endl;
        }
    }

    return 0;
}