org.apache.beam.sdk.io.synthetic.delay.SyntheticDelay.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.beam.sdk.io.synthetic.delay.SyntheticDelay.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.beam.sdk.io.synthetic.delay;

import com.google.common.base.Stopwatch;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.Uninterruptibles;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.beam.sdk.io.synthetic.SyntheticOptions;
import org.joda.time.Duration;

/** Utility functions used in {@link org.apache.beam.sdk.io.synthetic}. */
public class SyntheticDelay {

    // cpu delay implementation:

    private static final long MASK = (1L << 16) - 1L;
    private static final long HASH = 0x243F6A8885A308D3L;
    private static final long INIT_PLAINTEXT = 50000L;

    /**
     * Implements a mechanism to delay a thread in various fashions. * {@code CPU}: Burn CPU while
     * waiting. * {@code SLEEP}: Sleep uninterruptibly while waiting. * {@code MIXED}: Switch between
     * burning CPU and sleeping every millisecond to emulate a desired CPU utilization specified by
     * {@code cpuUtilizationInMixedDelay}.
     *
     * @return Millis spent sleeping, does not include time spent spinning.
     */
    public static long delay(Duration delay, double cpuUtilizationInMixedDelay,
            SyntheticOptions.DelayType delayType, Random rnd) {
        switch (delayType) {
        case CPU:
            cpuDelay(delay.getMillis());
            return 0;
        case SLEEP:
            Uninterruptibles.sleepUninterruptibly(Math.max(0L, delay.getMillis()), TimeUnit.MILLISECONDS);
            return delay.getMillis();
        case MIXED:
            // Mixed mode: for each millisecond of delay randomly choose to spin or sleep.
            // This is enforced at millisecond granularity since that is the minimum duration that
            // Thread.sleep() can sleep. Millisecond is also the unit of processing delay.
            long sleepMillis = 0;
            for (long i = 0; i < delay.getMillis(); i++) {
                if (rnd.nextDouble() < cpuUtilizationInMixedDelay) {
                    delay(new Duration(1), 0.0, SyntheticOptions.DelayType.CPU, rnd);
                } else {
                    sleepMillis += delay(new Duration(1), 0.0, SyntheticOptions.DelayType.SLEEP, rnd);
                }
            }
            return sleepMillis;
        default:
            throw new IllegalArgumentException("Unknown delay type " + delayType);
        }
    }

    /** Keep cpu busy for {@code delayMillis} by calculating lots of hashes. */
    private static void cpuDelay(long delayMillis) {
        // Note that the delay is enforced in terms of walltime. That implies this thread may not
        // keep CPU busy if it gets preempted by other threads. There is more of chance of this
        // occurring in a streaming pipeline as there could be lots of threads running this. The loop
        // measures cpu time spent for each iteration, so that these effects are some what minimized.

        long cpuMicros = delayMillis * 1000;
        Stopwatch timer = Stopwatch.createUnstarted();

        while (timer.elapsed(TimeUnit.MICROSECONDS) < cpuMicros) {
            // Find a long which hashes to HASH in lowest MASK bits.
            // Values chosen to roughly take 1ms on typical workstation.
            timer.start();
            long p = INIT_PLAINTEXT;
            while (true) {
                long t = Hashing.murmur3_128().hashLong(p).asLong();
                if ((t & MASK) == (HASH & MASK)) {
                    break;
                }
                p++;
            }
            timer.stop();
        }
    }
}