Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ArraySlice to slice a Vector #3724

Merged
merged 13 commits into from
Sep 23, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -655,15 +655,7 @@ type Vector a

[1, 2, 3, 4, 5, 6, 7, 8].slice 2 5 == [3, 4, 5]
slice : Integer -> Integer -> Vector Any
slice self start end =
slice_start = Math.max 0 start
slice_end = Math.min self.length end
if slice_start >= slice_end then [] else
if (slice_start == 0) && (slice_end == self.length) then self else
len = slice_end - slice_start
arr = Array.new len
Array.copy self.to_array slice_start arr 0 len
Vector.from_polyglot_array arr
slice self start end = @Builtin_Method "Vector.slice"

## Creates a new vector with only the specified range of elements from the
input, removing any elements outside the range.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,24 @@ public void initializeBenchmark(BenchmarkParams params) {
"\n" +
"to_vector arr = Vector.from_polyglot_array arr\n" +
"to_array vec = vec.to_array\n" +
"slice vec = vec.slice\n" +
"\n");

this.self = module.invokeMember("get_associated_type");
Function<String,Value> getMethod = (name) -> module.invokeMember("get_method", self, name);

Value arr = getMethod.apply("fibarr").execute(self, 1000, Integer.MAX_VALUE);
var length = 1000;
Value arr = getMethod.apply("fibarr").execute(self, length, Integer.MAX_VALUE);

switch (params.getBenchmark().replaceFirst(".*\\.", "")) {
case "averageOverVector": {
this.arrayOfFibNumbers = arr;
break;
}
case "averageOverSlice": {
this.arrayOfFibNumbers = getMethod.apply("slice").execute(self, arr, 1, length);
break;
}
case "averageOverArray": {
this.arrayOfFibNumbers = getMethod.apply("to_array").execute(self, arr);
break;
Expand Down Expand Up @@ -112,6 +118,11 @@ public void averageOverVector(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void averageOverSlice(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void averageOverPolyglotVector(Blackhole matter) {
performBenchmark(matter);
Expand All @@ -133,7 +144,8 @@ private void performBenchmark(Blackhole matter) throws AssertionError {
throw new AssertionError("Shall be a double: " + average);
}
var result = (long) average.asDouble();
if (result < 1019950590 || result > 1019950600) {
boolean isResultCorrect = (result >= 1019950590 && result <= 1019950600) || (result >= 1020971561 && result <= 1020971571);
if (!isResultCorrect) {
throw new AssertionError("Expecting reasonable average but was " + result + "\n" + arrayOfFibNumbers);
}
matter.consume(result);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.enso.interpreter.runtime.data;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;

@ExportLibrary(InteropLibrary.class)
class ArraySlice implements TruffleObject {
private final Object storage;
private final long start;
private final long end;

ArraySlice(Object storage, long start, long end) {
if (storage instanceof ArraySlice slice) {
jdunkerley marked this conversation as resolved.
Show resolved Hide resolved
this.storage = slice.storage;
this.start = slice.start + start;
this.end = Math.min(slice.end, slice.start + end);
} else {
if (CompilerDirectives.inInterpreter()) {
if (!InteropLibrary.getUncached().hasArrayElements(storage)) {
throw new IllegalStateException("ArraySlice needs array-like delegate, but got: " + storage);
}
}

this.storage = storage;
this.start = start;
this.end = end;
}
}

/**
* Marks the object as array-like for Polyglot APIs.
*
* @return {@code true}
*/
@ExportMessage
public boolean hasArrayElements() {
return true;
}

@ExportMessage
public long getArraySize(@CachedLibrary(limit = "3") InteropLibrary interop)
throws UnsupportedMessageException {
long storageSize = interop.getArraySize(storage);
return Math.max(0, Math.min(storageSize, end) - start);
}

/**
* Handles reading an element by index through the polyglot API.
*
* @param index the index to read
* @return the element value at the provided index
* @throws InvalidArrayIndexException when the index is out of bounds.
*/
@ExportMessage
public Object readArrayElement(
long index,
@CachedLibrary(limit = "3") InteropLibrary interop,
@Cached HostValueToEnsoNode toEnso)
throws InvalidArrayIndexException, UnsupportedMessageException {
if (index < 0 || index >= getArraySize(interop)) {
throw InvalidArrayIndexException.create(index);
}

var v = interop.readArrayElement(storage, start + index);
return toEnso.execute(v);
}

/**
* Exposes an index validity check through the polyglot API.
*
* @param index the index to check
* @return {@code true} if the index is valid, {@code false} otherwise.
*/
@ExportMessage
boolean isArrayElementReadable(long index, @CachedLibrary(limit = "3") InteropLibrary interop) {
try {
return index >= 0 && index < getArraySize(interop);
} catch (UnsupportedMessageException e) {
return false;
}
}

@ExportMessage
boolean isArrayElementModifiable(long index) {
return false;
}

@ExportMessage
final void writeArrayElement(long index, Object value)
throws UnsupportedMessageException {
throw UnsupportedMessageException.create();
}

@ExportMessage
boolean isArrayElementInsertable(long index) {
return false;
}

@ExportMessage
boolean isArrayElementRemovable(long index) {
return false;
}

@ExportMessage
final void removeArrayElement(long index) throws UnsupportedMessageException {
throw UnsupportedMessageException.create();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@
import com.oracle.truffle.api.library.ExportMessage;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.node.expression.builtin.error.PolyglotError;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNodeGen;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.DataflowError;
Expand Down Expand Up @@ -67,6 +63,26 @@ public final Object toArray() {
return this.storage;
}

@Builtin.Method(name = "slice", description = "Returns a slice of this Vector.")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
public final Vector slice(long start, long end, InteropLibrary interop)
jdunkerley marked this conversation as resolved.
Show resolved Hide resolved
throws UnsupportedMessageException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what circumstances is UnsupportedMessageException thrown?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only if the backing storage no longer can answer the interop.getArraySize call - shouldn't happen.

long this_length = length(interop);
long slice_start = Math.max(0, start);
long slice_end = Math.min(this_length, end);

if (slice_start >= slice_end) {
return new Vector(new Array(0));
}

if ((slice_start == 0) && (slice_end == this_length)) {
return this;
}

return new Vector(new ArraySlice(this.storage, slice_start, slice_end));
}

@Builtin.Method(description = "Returns the length of this Vector.")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
})
public class Types {

private static TypeGraph typeHierarchy = buildTypeHierarchy();
private static final TypeGraph typeHierarchy = buildTypeHierarchy();

/**
* A simple pair type
Expand All @@ -67,8 +67,8 @@ public class Types {
* @param <B> the type of the second element
*/
public static class Pair<A, B> {
private A first;
private B second;
private final A first;
private final B second;

private Pair(A first, B second) {
this.first = first;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* TypeWithKind provides a convenience wrapper for the types that can be encountered
Expand Down Expand Up @@ -30,13 +31,13 @@ boolean isValidGuestType() {
}

private final static List<String> primitiveTypes =
List.of(Boolean.TYPE, Long.TYPE, Double.TYPE, Float.TYPE).stream().map(Class::getSimpleName).collect(Collectors.toList());
Stream.of(Boolean.TYPE, Long.TYPE, Double.TYPE, Float.TYPE).map(Class::getSimpleName).collect(Collectors.toList());
/**
* A list of hard-coded types that can be used in the parameter or return type position
* that are valid host types i.e extend TruffleObject.
* Cannot go via reflection and check that they implement the interface, unfortunately.
*/
private static List<String> validGuestTypes =
private final static List<String> validGuestTypes =
List.of(
"org.enso.interpreter.runtime.callable.atom.Atom",
"org.enso.interpreter.runtime.callable.function.Function",
Expand Down
1 change: 1 addition & 0 deletions test/Benchmarks/src/Vector.enso
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base
radeusgd marked this conversation as resolved.
Show resolved Hide resolved

import Standard.Test.Bench

Expand Down
9 changes: 9 additions & 0 deletions test/Tests/src/Data/Vector_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ spec = Test.group "Vectors" <|
concat = [1, 2, 3] + [4, 5, 6]
concat.should_equal [1, 2, 3, 4, 5, 6]

Test.specify "Vector slice should return a Vector" <|
vec = [1, 2, 3, 4, 5, 6]
vec.slice 0 3 . should_equal [1, 2, 3]
vec.slice 1 3 . should_equal [2, 3]
vec.slice 1 1 . should_equal []
vec.slice 0 100 . should_equal [1, 2, 3, 4, 5, 6]
radeusgd marked this conversation as resolved.
Show resolved Hide resolved
Meta.is_same_object vec (vec.slice 0 100) . should_be_true
Meta.get_qualified_type_name vec . should_equal (Meta.get_qualified_type_name (vec.slice 1 1))

Test.specify "should define take and drop family of operations" <|
vec = [1, 2, 3, 4, 5, 6]
first_four = [1, 2, 3, 4]
Expand Down