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 codeSuggest 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 stringwhich 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
Post a Comment