Skip to content

FpKit now longer uses streams for performance reasons #3932

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

bbakerman
Copy link
Member

@bbakerman bbakerman commented Apr 27, 2025

We no longer uses .stream() since its slower than an imperative style.

The methods were lacking tests so I went all TDD on it and write tests fist with the old streams implementation and then changed it and watched the tests pass

@bbakerman bbakerman changed the title Revert "Revert "FpKit now longer uses streams for performance reasons"" FpKit now longer uses streams for performance reasons Apr 27, 2025
resultMap.put(key, obj);
}
}
return resultMap;
Copy link
Member Author

Choose a reason for hiding this comment

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

new common generic function - used in other places

Copy link
Contributor

github-actions bot commented Apr 27, 2025

Test Results

  312 files  ±0    312 suites  ±0   53s ⏱️ -1s
3 586 tests +5  3 581 ✅ +5  5 💤 ±0  0 ❌ ±0 
3 675 runs  +5  3 670 ✅ +5  5 💤 ±0  0 ❌ ±0 

Results for commit c81cee8. ± Comparison against base commit 262ff2f.

This pull request removes 173 and adds 156 tests. Note that renamed tests count towards both.
	?
                __schema { types { fields { args { type { name fields { name }}}}}}
                __schema { types { fields { type { name fields { name }}}}}
                __schema { types { inputFields { type { inputFields { name }}}}}
                __schema { types { interfaces { fields { type { interfaces { name } } } } } }
                __schema { types { name} }
                __type(name : "t") { name }
                a1: __schema { types { name} }
                a1: __type(name : "t") { name }
                a2 :  __type(name : "t1") { name }
…
graphql.DataFetcherTest ‑ get Boolean property value [fetcher: <graphql.schema.PropertyDataFetcher@7fd69dd propertyName=booleanField function=null>, #0]
graphql.DataFetcherTest ‑ get Boolean property value [fetcher: <graphql.schema.SingletonPropertyDataFetcher@33364212>, #1]
graphql.DataFetcherTest ‑ get Boolean property value with get [fetcher: <graphql.schema.PropertyDataFetcher@23940f86 propertyName=booleanFieldWithGet function=null>, #0]
graphql.DataFetcherTest ‑ get Boolean property value with get [fetcher: <graphql.schema.SingletonPropertyDataFetcher@33364212>, #1]
graphql.DataFetcherTest ‑ get property value [fetcher: <graphql.schema.SingletonPropertyDataFetcher@33364212>, #1]
graphql.DataFetcherTest ‑ get public field value as property [fetcher: <graphql.schema.PropertyDataFetcher@7318daf8 propertyName=publicField function=null>, #0]
graphql.DataFetcherTest ‑ get public field value as property [fetcher: <graphql.schema.SingletonPropertyDataFetcher@33364212>, #1]
graphql.ScalarsBooleanTest ‑ parseValue throws exception for invalid input <java.lang.Object@47e0adb4>
graphql.ScalarsBooleanTest ‑ serialize throws exception for invalid input <java.lang.Object@20094313>
graphql.ScalarsIDTest ‑ parseValue allows any object via String.valueOf <java.lang.Object@52b288a2>
…

♻️ This comment has been updated with latest results.

// The cleanest version of this code would have two maps, one of immutable list builders and one
// of the built immutable lists. BUt we are trying to be performant and memory efficient so
// we treat it as a map of objects and cast like its Java 4x
//
Copy link
Member Author

Choose a reason for hiding this comment

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

This comment above explains the whacky code. One less map allocation

Alternative is

public static <T, NewKey> Map<NewKey, ImmutableList<T>> groupingBy(Collection<T> list, Function<T, NewKey> function) {
        Map<NewKey, ImmutableList.Builder<T>> tempMap = new LinkedHashMap<>();
        for (T item : list) {
            NewKey key = function.apply(item);
            tempMap.computeIfAbsent(key, k -> ImmutableList.builder()).add(item);
        }
        Map<NewKey, ImmutableList<T>> resultMap = new LinkedHashMap<>();
        for (Map.Entry<NewKey, ImmutableList.Builder<T>> entry : tempMap.entrySet()) {
            resultMap.put(entry.getKey(), entry.getValue().build());
        }
        return resultMap;
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

Note two allocations - neater code - worse performance

LinkedHashMap::new)
);
public static <T, NewKey> Map<NewKey, T> toMapByUniqueKey(Collection<T> list, Function<T, NewKey> keyFunction) {
return toMap(list, keyFunction, throwingMerger());
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This was renamed from groupingByUniqueKey to toMapByUniqueKey because its NOT a group by at all. Its a toMap - it was badly named

@@ -240,11 +259,6 @@ public static <T> List<T> valuesToList(Map<?, T> map) {
return new ArrayList<>(map.values());
}

public static <K, V, U> List<U> mapEntries(Map<K, V> map, BiFunction<K, V, U> function) {
return map.entrySet().stream().map(entry -> function.apply(entry.getKey(), entry.getValue())).collect(Collectors.toList());
}
Copy link
Member Author

Choose a reason for hiding this comment

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

never used so I removed it - one less stream thing to port

throwingMerger(),
LinkedHashMap::new)
);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

never used - so I removed it - one less stream thing to port

@@ -98,6 +98,100 @@ class FpKitTest extends Specification {
l == ["Parrot"]
}

class Person {
Copy link
Member Author

Choose a reason for hiding this comment

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

hey look - TESTS!

We never had very many tests for FpKit

So I I write them first - then changed the implementation

TDD!!!

Map<NewKey, T> resultMap = new LinkedHashMap<>();
for (T obj : collection) {
NewKey key = keyFunction.apply(obj);
if (resultMap.containsKey(key)) {
Copy link
Member

Choose a reason for hiding this comment

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

Make it one less statement by looking up the value and save it one call

Copy link
Member Author

Choose a reason for hiding this comment

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

Technically not the right semantics in a general sense - sure there could be non

If this map permits null values, then a return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.

@bbakerman bbakerman added the performance work that is primarily targeted as performance improvements label Apr 27, 2025
@@ -261,21 +278,13 @@ public static <T> List<List<T>> transposeMatrix(List<? extends List<T>> matrix)
return result;
}

public static <T> CompletableFuture<List<T>> flatList(CompletableFuture<List<List<T>>> cf) {
return cf.thenApply(FpKit::flatList);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

never used - no need to port it

return listLists.stream()
.flatMap(List::stream)
.collect(ImmutableList.toImmutableList());
}
Copy link
Member Author

Choose a reason for hiding this comment

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

never used - no need to port it

@bbakerman bbakerman requested a review from dondonz April 27, 2025 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance work that is primarily targeted as performance improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants