-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Licensed 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 io.airlift.units; | ||
|
||
import javax.validation.Constraint; | ||
import javax.validation.Payload; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE; | ||
import static java.lang.annotation.ElementType.METHOD; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
@Target({METHOD, ANNOTATION_TYPE}) | ||
@Retention(RUNTIME) | ||
@Documented | ||
@Constraint(validatedBy = PowerOfTwoValidator.class) | ||
public @interface PowerOfTwo | ||
{ | ||
String message() default "is not a power of two"; | ||
|
||
Class<?>[] groups() default {}; | ||
|
||
Class<? extends Payload>[] payload() default {}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Licensed 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 io.airlift.units; | ||
|
||
import javax.validation.ConstraintValidator; | ||
import javax.validation.ConstraintValidatorContext; | ||
|
||
public class PowerOfTwoValidator | ||
implements ConstraintValidator<PowerOfTwo, Integer> | ||
{ | ||
@Override | ||
public void initialize(PowerOfTwo powerOfTwo) | ||
{ | ||
} | ||
|
||
@Override | ||
public boolean isValid(Integer value, ConstraintValidatorContext context) | ||
{ | ||
return value == null || Integer.bitCount(value) == 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/* | ||
* Licensed 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 io.airlift.units; | ||
|
||
import oshi.SystemInfo; | ||
|
||
import java.util.function.Supplier; | ||
|
||
import static io.airlift.units.Preconditions.checkArgument; | ||
import static java.lang.Math.min; | ||
import static java.lang.Math.round; | ||
import static java.lang.String.format; | ||
|
||
public class ThreadsCount | ||
implements Comparable<ThreadsCount> | ||
{ | ||
private static final String PER_CORE_SUFFIX = "C"; | ||
private static final Supplier<Integer> AVAILABLE_PROCESSORS = MachineInfo::getAvailablePhysicalProcessorCount; | ||
private final int threadsCount; | ||
|
||
ThreadsCount(int threadsCount) | ||
{ | ||
checkArgument(threadsCount >= 0, "Threads count cannot be smaller than 0"); | ||
this.threadsCount = threadsCount; | ||
} | ||
|
||
public int getThreadsCount() | ||
{ | ||
return threadsCount; | ||
} | ||
|
||
public ThreadsCount nextPowerOf2() | ||
{ | ||
return new ThreadsCount(nextPowerOfTwo(getThreadsCount())); | ||
} | ||
|
||
public static ThreadsCount exactValueOf(int value) | ||
{ | ||
return new ThreadsCount(value); | ||
} | ||
|
||
public static ThreadsCount valueOf(String value) | ||
{ | ||
if (value.endsWith(PER_CORE_SUFFIX)) { | ||
float parsedMultiplier = parseFloat(value.substring(0, value.lastIndexOf(PER_CORE_SUFFIX))); | ||
return new ThreadsCount(round(parsedMultiplier * AVAILABLE_PROCESSORS.get())); | ||
} | ||
|
||
return new ThreadsCount(parseInteger(value)); | ||
} | ||
|
||
public static ThreadsCount boundedValueOf(String value, String minValue, String maxValue) | ||
{ | ||
ThreadsCount parsed = ThreadsCount.valueOf(value); | ||
ThreadsCount min = ThreadsCount.valueOf(minValue); | ||
ThreadsCount max = ThreadsCount.valueOf(maxValue); | ||
|
||
if (parsed.compareTo(min) < 0) { | ||
return min; | ||
} | ||
|
||
if (parsed.compareTo(max) > 0) { | ||
return max; | ||
} | ||
return parsed; | ||
} | ||
|
||
private static float parseFloat(String value) | ||
{ | ||
try { | ||
return Float.parseFloat(value); | ||
} | ||
catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(format("Cannot parse value '%s' as float", value), e); | ||
} | ||
} | ||
|
||
private static int parseInteger(String value) | ||
{ | ||
try { | ||
return Integer.parseInt(value); | ||
} | ||
catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(format("Cannot parse value '%s' as integer", value), e); | ||
} | ||
} | ||
|
||
static int nextPowerOfTwo(int x) | ||
{ | ||
if (x == 0) { | ||
return 1; | ||
} | ||
|
||
--x; | ||
x |= x >> 1; | ||
x |= x >> 2; | ||
x |= x >> 4; | ||
x |= x >> 8; | ||
return (x | x >> 16) + 1; | ||
} | ||
|
||
@Override | ||
public int compareTo(ThreadsCount o) | ||
{ | ||
return Integer.compare(threadsCount, o.threadsCount); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) | ||
{ | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
|
||
ThreadsCount that = (ThreadsCount) o; | ||
return threadsCount == that.threadsCount; | ||
} | ||
|
||
@Override | ||
public int hashCode() | ||
{ | ||
return threadsCount; | ||
} | ||
|
||
@Override | ||
public String toString() | ||
{ | ||
if (threadsCount == 1) { | ||
return "1 thread"; | ||
} | ||
return format("%d threads", threadsCount); | ||
} | ||
|
||
public static final class MachineInfo | ||
{ | ||
// cache physical processor count, so that it's not queried multiple times during tests | ||
private static volatile int physicalProcessorCount = -1; | ||
|
||
private MachineInfo() {} | ||
|
||
public static int getAvailablePhysicalProcessorCount() | ||
{ | ||
if (physicalProcessorCount != -1) { | ||
return physicalProcessorCount; | ||
} | ||
|
||
String osArch = System.getProperty("os.arch"); | ||
// logical core count (including container cpu quota if there is any) | ||
int availableProcessorCount = Runtime.getRuntime().availableProcessors(); | ||
int totalPhysicalProcessorCount; | ||
if ("amd64".equals(osArch) || "x86_64".equals(osArch)) { | ||
// Oshi can recognize physical processor count (without hyper threading) for x86 platforms. | ||
// However, it doesn't correctly recognize physical processor count for ARM platforms. | ||
totalPhysicalProcessorCount = new SystemInfo() | ||
.getHardware() | ||
.getProcessor() | ||
.getPhysicalProcessorCount(); | ||
} | ||
else { | ||
// ARM platforms do not support hyper threading, therefore each logical processor is separate core | ||
totalPhysicalProcessorCount = availableProcessorCount; | ||
} | ||
|
||
// cap available processor count to container cpu quota (if there is any). | ||
physicalProcessorCount = min(totalPhysicalProcessorCount, availableProcessorCount); | ||
return physicalProcessorCount; | ||
} | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
src/test/java/io/airlift/units/TestPowerOfTwoValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* Licensed 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 io.airlift.units; | ||
|
||
import org.testng.annotations.Test; | ||
|
||
import static io.airlift.testing.ValidationAssertions.assertFailsValidation; | ||
import static io.airlift.testing.ValidationAssertions.assertValidates; | ||
|
||
public class TestPowerOfTwoValidator | ||
{ | ||
@Test | ||
public void testValidator() | ||
{ | ||
assertValid(1); | ||
assertValid(2); | ||
assertValid(64); | ||
assertInvalid(0); | ||
assertInvalid(3); | ||
assertInvalid(99); | ||
assertInvalid(-1); | ||
assertInvalid(-2); | ||
assertInvalid(-4); | ||
} | ||
|
||
@Test | ||
public void testAllowsNullPowerOfTwoAnnotation() | ||
{ | ||
assertValidates(new NullPowerOfTwoAnnotation()); | ||
} | ||
|
||
private static void assertValid(int value) | ||
{ | ||
assertValidates(new ConstrainedPowerOfTwo(value)); | ||
} | ||
|
||
private static void assertInvalid(int value) | ||
{ | ||
Object object = new ConstrainedPowerOfTwo(value); | ||
assertFailsValidation(object, "unboxed", "is not a power of two", PowerOfTwo.class); | ||
assertFailsValidation(object, "boxed", "is not a power of two", PowerOfTwo.class); | ||
} | ||
|
||
@SuppressWarnings("UnusedDeclaration") | ||
public static class ConstrainedPowerOfTwo | ||
{ | ||
private final int value; | ||
|
||
public ConstrainedPowerOfTwo(int value) | ||
{ | ||
this.value = value; | ||
} | ||
|
||
@PowerOfTwo | ||
public int getUnboxed() | ||
{ | ||
return value; | ||
} | ||
|
||
@PowerOfTwo | ||
public Integer getBoxed() | ||
{ | ||
return value; | ||
} | ||
} | ||
|
||
@SuppressWarnings("UnusedDeclaration") | ||
public static class NullPowerOfTwoAnnotation | ||
{ | ||
@PowerOfTwo | ||
public Integer getNull() | ||
{ | ||
return null; | ||
} | ||
} | ||
} |
Oops, something went wrong.