Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bc97c74
Only initialize BusinessCalendar caches when data is accessed.
chipkent Apr 17, 2024
4e2e878
Move the calendar to lazy ConcurrentHashMap initialization plus start…
chipkent Apr 17, 2024
f3fadc9
Added a thread-safe lazily initialized summary data cache by month an…
chipkent Apr 18, 2024
d39dec7
Using advanced caching in the BusinessCalendar v1.
chipkent Apr 18, 2024
52a4521
Fix javadoc warnings.
chipkent Apr 18, 2024
ccd9025
Using advanced caching in the BusinessCalendar v2.
chipkent Apr 18, 2024
9f07d91
Using advanced caching in the BusinessCalendar v3.
chipkent Apr 18, 2024
01377ab
Using advanced caching in the Calendar v4. Fixed unit tests.
chipkent Apr 18, 2024
c9be433
Fixed exclusive/inclusive problems.
chipkent Apr 18, 2024
3accd32
Spotless.
chipkent Apr 19, 2024
32f5c57
Spotless.
chipkent Apr 19, 2024
d1d96df
Copyright header.
chipkent Apr 19, 2024
fa69016
Copyright header.
chipkent Apr 19, 2024
f5e1e5f
Copyright header.
chipkent Apr 19, 2024
bf54c0f
Copyright header.
chipkent Apr 19, 2024
94fa753
A generalized fast concurrent cache.
chipkent Apr 23, 2024
9a47feb
Incorporating generalized cache.
chipkent Apr 23, 2024
d50afa4
Incorporating generalized cache.
chipkent Apr 24, 2024
5b0fe54
Incorporating generalized cache.
chipkent Apr 24, 2024
d0cde5a
Incorporating generalized cache.
chipkent Apr 24, 2024
b12453d
Configs to control fast cache population.
chipkent Apr 24, 2024
9a28928
Spotless
chipkent Apr 24, 2024
51138c7
Add a missing volatile.
chipkent Apr 25, 2024
2399510
More perf tuning.
chipkent Apr 25, 2024
c8e9fff
Fixed some locking problems.
chipkent Apr 25, 2024
3d5e500
Spotless
chipkent Apr 26, 2024
11725d7
Fast cache implementation based on a stamped lock.
chipkent May 17, 2024
cc8e4dc
Removed cache population and clean up code.
chipkent May 17, 2024
75283e7
Code self review.
chipkent May 17, 2024
359dc05
Spotless
chipkent May 17, 2024
4da4729
Moved to a KeyedIntObjectHash.
chipkent May 17, 2024
8e92d1e
Moved to a IntFunction.
chipkent May 17, 2024
050defa
Spotless
chipkent May 17, 2024
64911da
Addressing code review.
chipkent May 24, 2024
46320db
Addressing code review.
chipkent May 24, 2024
166074c
Addressing code review.
chipkent May 24, 2024
3a1c412
Addressing code review.
chipkent May 24, 2024
55d43a8
Made cache clearing package private.
chipkent Jun 18, 2024
38f6c0f
Reduced CalendarDay memory usage.
chipkent Jun 18, 2024
d1f88ec
Move businessNanos calculation to the constructor for a calendar day.
chipkent Jun 18, 2024
99abd9b
Simplify cache key type definition.
chipkent Jun 18, 2024
ab00dfc
Rename cache getter.
chipkent Jun 18, 2024
431a244
Remove poorly designed cache methods
chipkent Jun 18, 2024
46ab5bf
Consolidate year-monty key calculation.
chipkent Jun 18, 2024
5888eee
Consolidate cache key calculations.
chipkent Jun 18, 2024
43e4724
Address review comments.
chipkent Jun 24, 2024
4b26109
Merge remote-tracking branch 'origin/main' into bus_cal_init_speed
chipkent Aug 20, 2024
03ba3bd
Improve method naming.
chipkent Aug 20, 2024
d3416ba
Addressing review
chipkent Oct 3, 2024
d989315
Addressing review
chipkent Oct 3, 2024
cde9ab0
Addressing review
chipkent Oct 3, 2024
130a785
Merge remote-tracking branch 'upstream/main' into bus_cal_init_speed
chipkent Oct 3, 2024
e057f00
Addressing review
chipkent Oct 3, 2024
04d21ba
Addressing review
chipkent Oct 3, 2024
502bce0
Fix broken unit tests
chipkent Oct 9, 2024
1c33f1e
Merge remote-tracking branch 'origin/main' into bus_cal_init_speed
chipkent Oct 9, 2024
e77fa84
Addressing review comments
chipkent Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public SummaryData(
private final int yearCacheEnd;

@Override
public synchronized void clearCache() {
synchronized void clearCache() {
super.clearCache();
schedulesCache.clear();
summaryCache.clear();
Expand Down Expand Up @@ -148,12 +148,12 @@ private SummaryData summarize(final int key, final LocalDate startDate, final Lo
nonBusinessDates);
}

private SummaryData computeMonthSummary(final int yearMonth) {
final int year = yearMonth / 100;
final int month = yearMonth % 100;
private SummaryData computeMonthSummary(final int key) {
final int year = YearMonthSummaryCache.yearFromMonthKey(key);
final int month = YearMonthSummaryCache.monthFromMonthKey(key);
final LocalDate startDate = LocalDate.of(year, month, 1);
final LocalDate endDate = startDate.plusMonths(1); // exclusive
return summarize(yearMonth, startDate, endDate);
return summarize(key, startDate, endDate);
}

private SummaryData computeYearSummary(final int year) {
Expand Down Expand Up @@ -208,13 +208,28 @@ private SummaryData getYearSummary(final int year) {
return summaryCache.getYearSummary(year);
}

/**
* Creates a key for the schedules cache.
*
* @param date date
* @return key
*/
static int schedulesCacheKey(final LocalDate date) {
return date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth();
}

/**
* Creates a date from a schedules cache key.
*
* @param key key
* @return date
*/
static LocalDate dateFromSchedulesCacheKey(final int key) {
return LocalDate.of(key / 10000, (key % 10000) / 100, key % 100);
}

private ImmutableConcurrentCache.Pair<CalendarDay<Instant>> computeCalendarDay(final int yearMonthDay) {
final int year = yearMonthDay / 10000;
final int monthDay = yearMonthDay % 10000;
final int month = monthDay / 100;
final int day = monthDay % 100;
final LocalDate date = LocalDate.of(year, month, day);
private ImmutableConcurrentCache.Pair<CalendarDay<Instant>> computeCalendarDay(final int key) {
final LocalDate date = dateFromSchedulesCacheKey(key);
final CalendarDay<Instant> h = holidays.get(date);
final CalendarDay<Instant> v;

Expand All @@ -226,7 +241,7 @@ private ImmutableConcurrentCache.Pair<CalendarDay<Instant>> computeCalendarDay(f
v = CalendarDay.toInstant(standardBusinessDay, date, timeZone());
}

return new ImmutableConcurrentCache.Pair<>(yearMonthDay, v);
return new ImmutableConcurrentCache.Pair<>(key, v);
}

// endregion
Expand Down Expand Up @@ -358,8 +373,8 @@ public CalendarDay<Instant> calendarDay(final LocalDate date) {
+ " lastValidDate=" + lastValidDate);
}

final int yearMonthDay = date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth();
return schedulesCache.get(yearMonthDay).getValue();
final int key = schedulesCacheKey(date);
return schedulesCache.computeIfAbsent(key).getValue();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private SummaryData computeYearSummary(final int year) {
/**
* Clears the cache. This should not generally be used and is provided for benchmarking.
*/
public synchronized void clearCache() {
synchronized void clearCache() {
summaryCache.clear();
}
Comment thread
chipkent marked this conversation as resolved.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public class CalendarDay<T extends Comparable<T> & Temporal> {
public static final CalendarDay<LocalTime> HOLIDAY = new CalendarDay<>();

private final TimeRange<T>[] businessTimeRanges;
private final List<TimeRange<T>> businessTimeRangesList;
private volatile long businessNanos = -1;
private final long businessNanos;

/**
* Creates a CalendarDay instance.
Expand Down Expand Up @@ -66,7 +65,7 @@ public class CalendarDay<T extends Comparable<T> & Temporal> {
}

this.businessTimeRanges = ranges;
this.businessTimeRangesList = List.of(ranges);
this.businessNanos = Arrays.stream(businessTimeRanges).map(TimeRange::nanos).reduce(0L, Long::sum);
Comment thread
chipkent marked this conversation as resolved.
Outdated
}

/**
Expand All @@ -83,7 +82,7 @@ public class CalendarDay<T extends Comparable<T> & Temporal> {
* @return business time ranges for the day
*/
public List<TimeRange<T>> businessTimeRanges() {
return businessTimeRangesList;
return List.of(businessTimeRanges);
Comment thread
chipkent marked this conversation as resolved.
Outdated
}

/**
Expand Down Expand Up @@ -120,15 +119,7 @@ public boolean isInclusiveEnd() {
* @return length of the day in nanoseconds
*/
public long businessNanos() {
// Only read volatile 1x
long bn = businessNanos;

if (bn < 0) {
bn = Arrays.stream(businessTimeRanges).map(TimeRange::nanos).reduce(0L, Long::sum);
businessNanos = bn;
}

return bn;
return businessNanos;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,13 @@ public T getValue() {
}
}

private static class KeyDef<V extends IntKeyedValue> extends KeyedIntObjectKey.Strict<V> {
private static class KeyDef<V extends IntKeyedValue> extends KeyedIntObjectKey.BasicStrict<V> {

@Override
public int getIntKey(V v) {
return v.getKey();
}

@Override
public int hashIntKey(int i) {
return i;
}

@Override
public boolean equalIntKey(int i, V v) {
return i == v.getKey();
}
}

private final IntFunction<V> valueComputer;
Expand Down Expand Up @@ -116,7 +107,7 @@ synchronized void clear() {
* @return the value
* @throws IllegalArgumentException if the value is not found
*/
public V get(int key) {
public V computeIfAbsent(int key) {
V existing = cache.get(key);

if (existing != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,37 @@
*/
class YearMonthSummaryCache<T extends ImmutableConcurrentCache.IntKeyedValue> {

/**
* Computes a key for a year and month.
*
* @param year the year
* @param month the month
* @return the key
*/
static int monthKey(int year, int month) {
return year * 100 + month;
}

/**
* Gets the year from a year-month key.
*
* @param key the year month key
* @return the year
*/
static int yearFromMonthKey(int key) {
return key / 100;
}

/**
* Gets the month from a year-month key.
*
* @param key the year month key
* @return the month
*/
static int monthFromMonthKey(int key) {
return key % 100;
}

private final ImmutableConcurrentCache<T> monthCache;
private final ImmutableConcurrentCache<T> yearCache;

Expand All @@ -36,16 +67,6 @@ synchronized void clear() {
yearCache.clear();
}

/**
* Gets the month summary for the specified year and month.
*
* @param yearMonth the year and month
* @return the month summary
*/
T getMonthSummary(int yearMonth) {
return monthCache.get(yearMonth);
}

/**
* Gets the month summary for the specified year and month.
*
Expand All @@ -54,7 +75,7 @@ T getMonthSummary(int yearMonth) {
* @return the month summary
*/
T getMonthSummary(int year, int month) {
return getMonthSummary(year * 100 + month);
return monthCache.computeIfAbsent(monthKey(year, month));
}

/**
Expand All @@ -64,7 +85,7 @@ T getMonthSummary(int year, int month) {
* @return the year summary
*/
T getYearSummary(int year) {
return yearCache.get(year);
return yearCache.computeIfAbsent(year);
}

private class YearMonthSummaryIterator implements Iterator<T> {
Expand All @@ -89,7 +110,7 @@ private class YearMonthSummaryIterator implements Iterator<T> {
incrementCurrentByMonth();
}

currentYearMonth = currentYear * 100 + currentMonth;
currentYearMonth = monthKey(currentYear, currentMonth);

final LocalDate endPlus1 = end.plusDays(1);
final int endPlus1Month = endPlus1.getMonthValue();
Expand All @@ -106,7 +127,7 @@ private class YearMonthSummaryIterator implements Iterator<T> {
}
}

finalYearMonth = finalYear * 100 + finalMonth;
finalYearMonth = monthKey(finalYear, finalMonth);
}

private void incrementCurrentByMonth() {
Expand All @@ -117,12 +138,12 @@ private void incrementCurrentByMonth() {
currentMonth = currentMonth + 1;
}

currentYearMonth = currentYear * 100 + currentMonth;
currentYearMonth = monthKey(currentYear, currentMonth);
}

private void incrementCurrentByYear() {
currentYear++;
currentYearMonth = currentYear * 100 + currentMonth;
currentYearMonth = monthKey(currentYear, currentMonth);
}

@Override
Expand All @@ -138,7 +159,7 @@ public T next() {
val = getYearSummary(currentYear);
incrementCurrentByYear();
} else {
val = getMonthSummary(currentYear * 100 + currentMonth);
val = getMonthSummary(currentYear, currentMonth);
incrementCurrentByMonth();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ protected void setUp() throws Exception {
calendar = bCalendar;
}

public void testSchedulesCacheKeys() {
final int y = 2023;
final int m = 7;
final int d = 11;
final LocalDate ld = LocalDate.of(y, m, d);

final int key = BusinessCalendar.schedulesCacheKey(ld);
assertEquals(key, y * 10000 + m * 100 + d);
assertEquals(ld, BusinessCalendar.dateFromSchedulesCacheKey(key));
}

public void testBusinessGetters() {
assertEquals(schedule, bCalendar.standardBusinessDay());
assertEquals(schedule.businessNanos(), bCalendar.standardBusinessNanos());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,27 @@ public void testCache() {
final ImmutableConcurrentCache<Value> cache =
new ImmutableConcurrentCache<>(10, TestImmutableConcurrentCache::makeVal);

assertEquals("A", cache.get(0).getValue());
assertEquals("A", cache.get(0).getValue());
assertEquals("A", cache.computeIfAbsent(0).getValue());
assertEquals("A", cache.computeIfAbsent(0).getValue());

assertEquals("A", cache.get(0).getValue());
assertEquals("B", cache.get(1).getValue());
assertEquals("C", cache.get(2).getValue());
assertEquals("A", cache.computeIfAbsent(0).getValue());
assertEquals("B", cache.computeIfAbsent(1).getValue());
assertEquals("C", cache.computeIfAbsent(2).getValue());

try {
cache.get(3);
cache.computeIfAbsent(3);
fail("Expected exception");
} catch (final IllegalArgumentException e) {
// pass
}

cache.clear();
assertEquals("A", cache.get(0).getValue());
assertEquals("B", cache.get(1).getValue());
assertEquals("C", cache.get(2).getValue());
assertEquals("A", cache.computeIfAbsent(0).getValue());
assertEquals("B", cache.computeIfAbsent(1).getValue());
assertEquals("C", cache.computeIfAbsent(2).getValue());

try {
cache.get(3);
cache.computeIfAbsent(3);
fail("Expected exception");
} catch (final IllegalArgumentException e) {
// pass
Expand Down
Loading