Improving the prompt to the AI to get better code

In a previous article I looked at one-shoting a solution to optimise code to show the variation in different AI. Thsi is the not the best way to get what you want however. More often you need to either refine the prompt or give feedback. After one-shoting the same prompt on multiple AI, I have created a refined prompt based on the various concerns with previous results.

The prompt

Based on the results in a previous run Asking multiple AI to optimise the same code
Suggest how to implement this more optimally using low latency techniques 
to minimize any objects created.

## Use
- a ThreadLocal for temporary data.
- simple maths rather than a library, add comments for clarity if needed.
- offset in the form ±hh, ±hhmm, or ±hhmmss, using the shortest form that 
does not lose information, where hh, mm, and ss are the hours, minutes, 
and seconds east (+) or west (-) of UT 
- return a `intern()` String.

## Don't use
- String.format
- String operations that create objects.
- any colons, they aren't required
- reduce code duplication

## The code
 
private static String formatOffset(int millis) {
    String sign = millis < 0 ? "-" : "+";
    int saveSecs = Math.abs(millis) / 1000;
    int hours = saveSecs / 3600;
    int mins = ((saveSecs / 60) % 60);
    int secs = (saveSecs % 60);
    if (secs == 0) {
        if (mins == 0) {
            return sign + twoDigitString(hours);
        }
        return sign + twoDigitString(hours) + twoDigitString(mins);
    }
    return sign + twoDigitString(hours) + twoDigitString(mins) + twoDigitString(secs);
}

private static String twoDigitString(int value) {
    return Integer.toString(value + 100).substring(1);
}
I typically use asciidoc rather than markdown, but trying to keep this example simple.

None of the AI considered using a byte[] event though String now uses a byte[] as an underlying store. I asked each one to

change the implementation to use a byte[] to create an ISO_8859_1 encoded string
which they were able to do, but none suggested it.

Gemini 2.5 pro

This result is signifciantly improved. Use use of StringBuilder is more idomatic, but adds overhead. I find Gemini tends to produce wordier results.
    /**
     * A ThreadLocal holding a StringBuilder instance for each thread.
     * This avoids creating a new StringBuilder on each method call, reducing
     * object churn and garbage collection pressure. The initial capacity is
     * set to 7, the maximum length of the output string (e.g., "-hhmmss").
     */
    private static final ThreadLocal<StringBuilder> OFFSET_BUILDER =
        ThreadLocal.withInitial(() -> new StringBuilder(7));

    /**
     * Formats a timezone offset from milliseconds into the shortest possible
     * ISO 8601 format (±hh, ±hhmm, or ±hhmmss).
     *
     * This implementation is optimized to minimize object creation by using a
     * ThreadLocal StringBuilder and simple integer arithmetic.
     *
     * @param millis The offset from UTC in milliseconds.
     * @return A string representation of the offset, fetched from the string pool.
     */
    public static String formatOffset(int millis) {
        // Get the reusable StringBuilder for the current thread and clear it.
        StringBuilder sb = OFFSET_BUILDER.get();
        sb.setLength(0);

        // Determine the sign and work with absolute values.
        final char sign = millis < 0 ? '-' : '+';
        final int totalSeconds = Math.abs(millis) / 1000;

        // Decompose the total seconds into hours, minutes, and seconds.
        // 3600 seconds in an hour.
        final int hours = totalSeconds / 3600;
        // 60 seconds in a minute.
        final int minutes = (totalSeconds / 60) % 60;
        final int seconds = totalSeconds % 60;

        // Append the sign character.
        sb.append(sign);

        // Append the hours, padded to two digits.
        appendTwoDigits(sb, hours);

        // Only include minutes and seconds if they are non-zero,
        // using the shortest format possible.
        if (minutes == 0 && seconds == 0) {
            // Format is ±hh, so we are done.
            return sb.toString().intern();
        }

        // Append the minutes, padded to two digits.
        appendTwoDigits(sb, minutes);

        if (seconds == 0) {
            // Format is ±hhmm, so we are done.
            return sb.toString().intern();
        }

        // Append the seconds, padded to two digits, for the ±hhmmss format.
        appendTwoDigits(sb, seconds);

        // Return the final string from the shared pool.
        return sb.toString().intern();
    }

    /**
     * Appends an integer value to a StringBuilder, padded with a leading zero
     * if it is less than 10. This is a fast, object-free alternative to
     * String.format or Integer.toString.
     *
     * @param sb    The StringBuilder to append to.
     * @param value The integer value (0-99).
     */
    private static void appendTwoDigits(StringBuilder sb, int value) {
        // By adding '0', we convert the integer result of the division/modulo
        // into its ASCII character equivalent.
        // Example for value=7: '0' + 0 = '0', '0' + 7 = '7' -> "07"
        // Example for value=12: '0' + 1 = '1', '0' + 2 = '2' -> "12"
        sb.append((char) ('0' + value / 10));
        sb.append((char) ('0' + value % 10));
    }

o3-pro

This took the longest, 18m 20s.
/** Thread‑local scratch – long enough for “-235959” (7 chars inc. sign) */
private static final ThreadLocal<char[]> BUF =
        ThreadLocal.withInitial(() -> new char[7]);

/**
 * Format an offset in milliseconds as ±hh[mm[ss]] with no separators.
 * The returned String is interned so repeated calls for the same offset
 * are pointer‑equal.
 */
public static String formatOffset(int millis) {
    char[] buf = BUF.get();
    int pos = 0;

    // sign ---------------------------------------------------------------
    if (millis < 0) {
        buf[pos++] = '-';
        millis = -millis;   // safe: abs(Integer.MIN_VALUE) won’t hit here
    } else {
        buf[pos++] = '+';
    }

    // breakdown ---------------------------------------------------------
    int totalSecs = millis / 1_000;
    int hours    =  totalSecs / 3_600;
    int minutes  = (totalSecs /   60) % 60;
    int seconds  =  totalSecs         % 60;

    pos = writeTwoDigits(buf, pos, hours);

    if (minutes == 0 && seconds == 0)
        return new String(buf, 0, pos).intern();

    pos = writeTwoDigits(buf, pos, minutes);

    if (seconds == 0)
        return new String(buf, 0, pos).intern();

    pos = writeTwoDigits(buf, pos, seconds);
    return new String(buf, 0, pos).intern();
}

/** Writes exactly two ASCII digits into buf and returns new position. */
private static int writeTwoDigits(char[] buf, int pos, int value) {
    buf[pos++] = (char) ('0' + value / 10);
    buf[pos++] = (char) ('0' + value % 10);
    return pos;
}

o4-mini-high

This soltuion is good but has more duplication than I would like. This took 17 seconds.
private static final ThreadLocal<char[]> OFFSET_BUF =
    ThreadLocal.withInitial(() -> new char[7]); // sign + hh + mm + ss

/**
 * Format a UTC offset in ±hh, ±hhmm or ±hhmmss (no colons),
 * using the shortest form that contains all non-zero fields.
 * Returns an interned String so each distinct offset is pooled.
 *
 * @param offsetMillis signed offset from UTC in milliseconds
 * @return offset string, e.g. "+02", "-0530", "+023015"
 */
public static String formatOffset(int offsetMillis) {
    char[] buf = OFFSET_BUF.get();

    // Convert to total seconds and extract sign
    int totalSecs = offsetMillis / 1000;
    char sign = totalSecs < 0 ? '-' : '+';
    if (totalSecs < 0) totalSecs = -totalSecs;

    // Hours, minutes, seconds by simple div/mod math
    int hours   = totalSecs / 3600;
    int rem     = totalSecs - hours * 3600;
    int minutes = rem / 60;
    int seconds = rem - minutes * 60;

    // Fill buffer
    int pos = 0;
    buf[pos++] = sign;
    // always two digits for hours
    buf[pos++] = (char)('0' + (hours / 10));
    buf[pos++] = (char)('0' + (hours % 10));

    if (seconds == 0) {
        if (minutes == 0) {
            // ±hh
            return new String(buf, 0, pos).intern();
        }
        // ±hhmm
        buf[pos++] = (char)('0' + (minutes / 10));
        buf[pos++] = (char)('0' + (minutes % 10));
        return new String(buf, 0, pos).intern();
    }

    // ±hhmmss
    buf[pos++] = (char)('0' + (minutes / 10));
    buf[pos++] = (char)('0' + (minutes % 10));
    buf[pos++] = (char)('0' + (seconds / 10));
    buf[pos++] = (char)('0' + (seconds % 10));
    return new String(buf, 0, pos).intern();
}

Claude 4

The improved prompt improved the solution which was already pretty good. This took a 1m,40s.
// ThreadLocal buffer reused across calls to minimize object creation
private static final ThreadLocal<char[]> BUFFER = ThreadLocal.withInitial(() -> new char[7]);

private static String formatOffset(int millis) {
    char[] buf = BUFFER.get();
    int pos = 0;
    
    // Add sign: + for positive/zero, - for negative
    buf[pos++] = millis < 0 ? '-' : '+';
    
    // Convert to positive seconds for calculations
    int totalSeconds = Math.abs(millis) / 1000;
    
    // Extract hours, minutes, seconds using simple division/modulo
    int hours = totalSeconds / 3600;          // 3600 seconds per hour
    int mins = (totalSeconds / 60) % 60;      // 60 seconds per minute, mod 60 for remainder
    int secs = totalSeconds % 60;             // Remaining seconds
    
    // Hours are always included (±hh minimum format)
    buf[pos++] = (char)('0' + hours / 10);    // Tens digit
    buf[pos++] = (char)('0' + hours % 10);    // Units digit
    
    // Include minutes if non-zero or if seconds are non-zero
    if (mins != 0 || secs != 0) {
        buf[pos++] = (char)('0' + mins / 10);
        buf[pos++] = (char)('0' + mins % 10);
        
        // Include seconds only if non-zero
        if (secs != 0) {
            buf[pos++] = (char)('0' + secs / 10);
            buf[pos++] = (char)('0' + secs % 10);
        }
    }
    
    // Create final string from buffer and intern for memory efficiency
    return new String(buf, 0, pos).intern();
}

Grok 3 Think

This result is much better with a refined prompt. As noted before Grok 4 should be better. This took a 53 seconds.
// Size of 9 covers worst-case scenario: +hhmmss (7 chars) plus potential padding.
private static final ThreadLocal<char[]> BUFFER = ThreadLocal.withInitial(() -> new char[9]);

private static String formatOffset(int millis) {
    // Use ThreadLocal char array to avoid StringBuilder or new String creation
    char[] buf = BUFFER.get();
    int pos = 0;

    // Determine sign and absolute seconds
    int absMillis = Math.abs(millis);
    buf[pos++] = millis < 0 ? '-' : '+';
    int totalSecs = absMillis / 1000;

    // Calculate hours, minutes, seconds using division and modulo
    int hours = totalSecs / 3600;
    int remainingSecs = totalSecs % 3600;
    int mins = remainingSecs / 60;
    int secs = remainingSecs % 60;

    // Convert hours to two digits
    buf[pos++] = (char) ('0' + hours / 10);
    buf[pos++] = (char) ('0' + hours % 10);

    // Skip minutes and seconds if zero for shortest representation
    if (mins == 0 && secs == 0) {
        return new String(buf, 0, pos).intern();
    }

    // Append minutes
    buf[pos++] = (char) ('0' + mins / 10);
    buf[pos++] = (char) ('0' + mins % 10);

    // Skip seconds if zero
    if (secs == 0) {
        return new String(buf, 0, pos).intern();
    }

    // Append seconds
    buf[pos++] = (char) ('0' + secs / 10);
    buf[pos++] = (char) ('0' + secs % 10);

    return new String(buf, 0, pos).intern();
}
After being asked to use a byte[] it used a byte[9]

GitHub Copilot with GPT 4.1

This produced a much better result with the more detailed prompt. Note that it uses ±h instead of always using ±hh. This took a few seconds.
private static final ThreadLocal<char[]> OFFSET_BUFFER = ThreadLocal.withInitial(() -> new char[7]); // sign + 2*3 digits

private static String formatOffset(int millis) {
    char[] buf = OFFSET_BUFFER.get();
    int absMillis = millis < 0 ? -millis : millis;
    int totalSecs = absMillis / 1000;
    int hours = totalSecs / 3600;
    int mins = (totalSecs / 60) % 60;
    int secs = totalSecs % 60;

    int pos = 0;
    buf[pos++] = millis < 0 ? '-' : '+';

    // Write hours
    if (hours < 10) {
        buf[pos++] = (char) ('0' + hours);
    } else {
        buf[pos++] = (char) ('0' + hours / 10);
        buf[pos++] = (char) ('0' + hours % 10);
    }

    // Only add minutes if needed
    if (mins != 0 || secs != 0) {
        buf[pos++] = (char) ('0' + mins / 10);
        buf[pos++] = (char) ('0' + mins % 10);
    }

    // Only add seconds if needed
    if (secs != 0) {
        buf[pos++] = (char) ('0' + secs / 10);
        buf[pos++] = (char) ('0' + secs % 10);
    }

    // Return interned String
    return new String(buf, 0, pos).intern();
}

Microsoft Copilot with Think Deeper

This is significantly better than the simple prompt. This took a few seconds.
public class OffsetFormatter {

    // Thread-local buffer: max length 7 ("±hhmmss")
    private static final ThreadLocal<char[]> BUF =
        ThreadLocal.withInitial(() -> new char[7]);

    /**
     * Formats a UTC offset in milliseconds to a string in the form:
     *  ±hh, ±hhmm, or ±hhmmss using the shortest form
     *  without losing information. Returns an interned String.
     */
    public static String formatOffset(int offsetMillis) {
        char[] buf = BUF.get();
        // 0: sign, 1–2: hh, 3–4: mm, 5–6: ss
        buf[0] = offsetMillis < 0 ? '-' : '+';

        // total seconds east of UT
        int totalSeconds = Math.abs(offsetMillis) / 1000;

        // compute hours / minutes / seconds
        int hours = totalSeconds / 3600;
        int rem    = totalSeconds - hours * 3600;
        int mins   = rem / 60;
        int secs   = rem - mins * 60;

        // write two-digit hours
        writeTwoDigits(buf, 1, hours);

        if (secs == 0) {
            if (mins == 0) {
                // ±hh
                return new String(buf, 0, 3).intern();
            }
            // write two-digit minutes (±hhmm)
            writeTwoDigits(buf, 3, mins);
            return new String(buf, 0, 5).intern();
        }

        // write two-digit minutes and seconds (±hhmmss)
        writeTwoDigits(buf, 3, mins);
        writeTwoDigits(buf, 5, secs);
        return new String(buf, 0, 7).intern();
    }

    /** 
     * Writes a zero-padded two-digit number into buf at position idx:
     *   buf[idx]   = tens digit
     *   buf[idx+1] = ones digit
     */
    private static void writeTwoDigits(char[] buf, int idx, int value) {
        buf[idx]   = (char) ('0' + (value / 10));
        buf[idx+1] = (char) ('0' + (value % 10));
    }
}

Conclusion

It's worth noting that;
  • None suggested using a byte[] though they could all do that.
  • To reduce code duplication some uses a helper method or called new String once. None did both optimisations.
  • The more detailed prompt helped the AI which gave poor answers before bridging the gap in the results.
  • The AI which took significantly longer, didn't produce a significantly better answer in this case.

Comments

Popular posts from this blog

Java is Very Fast, If You Don’t Create Many Objects

Hands-On Career: The Evolution of a Java Champion

Demystifying Java Object Sizes: Compact Headers, Compressed Oops, and Beyond