java-topology/defects/transformers-0003/test/Transformers0003ConvertTokensSpecialListTest.java
russell@unturf.com 81bef63b2e transformers+vllm: 3 new defects, all 5 MOADs scanned
transformers-0002: MOAD-0004 (CWE-312) regnet convert script logs HF_TOKEN verbatim
transformers-0003: MOAD-0001 (CWE-407) convert_tokens_to_string O(T×S) list scan
  - marian, m2m_100, speech_to_text, siglip, gpt_sw3 all affected
  - all_special_tokens is list[str]; fix: cache set() before loop; 5x speedup

vllm-0002: MOAD-0001 (CWE-407) Grok2Tokenizer O(N×V) dict.values() scan
  - decode() and convert_ids_to_tokens() use .values() view per token
  - sibling Mistral tokenizer already uses frozenset correctly
  - fix: add _special_token_ids frozenset at __init__; 10x speedup at N=2048, V=200

MOADs 0002/0003/0005 CLEAN for both repos
2026-03-31 20:17:09 -04:00

113 lines
4.5 KiB
Java
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import java.util.*;
/**
* transformers-0003: convert_tokens_to_string O(T×S) list scan vs set lookup
*
* Models marian/m2m100/speech_to_text convert_tokens_to_string():
* DEFECTIVE: for each token, call all_special_tokens property (list) → O(T×S)
* PATCHED: cache set(all_special_tokens) before loop → O(T)
*
* Mirrors the pattern at:
* src/transformers/models/marian/tokenization_marian.py ~line 283
* src/transformers/models/m2m_100/tokenization_m2m_100.py ~line 216
* src/transformers/models/speech_to_text/tokenization_speech_to_text.py ~line 193
*/
public class Transformers0003ConvertTokensSpecialListTest {
// Simulate `all_special_tokens` property — returns a new list each call
// (in Python, the property calls convert_tokens_to_ids which reconstructs)
static List<String> allSpecialTokensList(List<String> specialTokens) {
return new ArrayList<>(specialTokens); // new list each time (mirrors Python property)
}
// --- Defective implementation ---
static String convertTokensDefective(List<String> tokens, List<String> specialTokens) {
StringBuilder current = new StringBuilder();
StringBuilder out = new StringBuilder();
for (String token : tokens) {
// O(S) list scan per token — mirrors `if token in self.all_special_tokens`
if (allSpecialTokensList(specialTokens).contains(token)) {
out.append(current).append(token).append(" ");
current.setLength(0);
} else {
current.append(token);
}
}
out.append(current);
return out.toString().trim();
}
// --- Patched implementation ---
static String convertTokensPatched(List<String> tokens, List<String> specialTokens) {
Set<String> specialSet = new HashSet<>(specialTokens); // cache once
StringBuilder current = new StringBuilder();
StringBuilder out = new StringBuilder();
for (String token : tokens) {
if (specialSet.contains(token)) { // O(1)
out.append(current).append(token).append(" ");
current.setLength(0);
} else {
current.append(token);
}
}
out.append(current);
return out.toString().trim();
}
public static void main(String[] args) {
// Build special tokens list: 5 base tokens + 100 language codes (m2m100 style)
int S = 108;
List<String> specialTokens = new ArrayList<>();
specialTokens.add("<unk>");
specialTokens.add("<s>");
specialTokens.add("</s>");
specialTokens.add("<pad>");
specialTokens.add("<mask>");
for (int i = 0; i < 103; i++) {
specialTokens.add("__lang" + i + "__");
}
// Build output token sequence: T=512, with a few special tokens mixed in
int T = 512;
List<String> tokens = new ArrayList<>(T);
for (int i = 0; i < T; i++) {
if (i % 50 == 0) {
tokens.add("__lang0__"); // insert a special token
} else {
tokens.add("word" + (i % 1000));
}
}
// Correctness check
String defectOut = convertTokensDefective(tokens, specialTokens);
String patchOut = convertTokensPatched(tokens, specialTokens);
assert defectOut.equals(patchOut)
: "FAIL: defect and patched outputs differ:\n defect=" + defectOut.substring(0, 50)
+ "\n patch=" + patchOut.substring(0, 50);
// Timing comparison
int REPS = 2000;
long t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
convertTokensDefective(tokens, specialTokens);
}
long defectNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
convertTokensPatched(tokens, specialTokens);
}
long patchNs = System.nanoTime() - t1;
double ratio = (double) defectNs / patchNs;
System.out.printf("transformers-0003 MOAD-0001 convert_tokens_to_string O(T×S)%n");
System.out.printf(" T=%d tokens, S=%d special tokens, %d reps%n", T, S, REPS);
System.out.printf(" Defective (list scan + property call): %,d ms%n", defectNs / 1_000_000);
System.out.printf(" Patched (set cache before loop): %,d ms%n", patchNs / 1_000_000);
System.out.printf(" Speedup: %.1fx%n", ratio);
assert ratio > 3.0 : "FAIL: expected >3x speedup, got " + ratio;
System.out.println("PASS");
}
}