test(bench): add automated benchmark scripts and document baseline metrics

This commit is contained in:
Claudecio Martins
2026-10-20 15:47:38 -03:00
parent 71428cdaad
commit bac691aae1
4 changed files with 371 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
package scripts.bench;
import br.com.cybercore.signer.core.domain.ManagedCertificate;
import br.com.cybercore.signer.core.pdf.PdfSealPosition;
import br.com.cybercore.signer.core.pdf.PdfSignatureOptions;
import br.com.cybercore.signer.core.pdf.PdfSigningService;
import br.com.cybercore.signer.core.provider.CertificateManager;
import br.com.cybercore.signer.core.provider.Pkcs12CertificateProvider;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
/**
* Utilitário de benchmark para medição de latência de certificados e assinatura PAdES (100KB, 5MB e 50MB).
*/
public class BenchmarkRunner {
private static final String TARGET_DIR = "target/bench-data";
public static void main(String[] args) throws Exception {
System.out.println("===============================================================");
System.out.println(" CyberCore Signer — Benchmark de Desempenho (Certificados & PAdES)");
System.out.println("===============================================================");
File dir = new File(TARGET_DIR);
if (!dir.exists()) {
dir.mkdirs();
}
// 1. Prepara Certificado A1 de Teste
System.out.print("[1/3] Inicializando provedor e certificado A1 de teste... ");
long tCertGenStart = System.currentTimeMillis();
CertificateManager certManager = new CertificateManager();
Pkcs12CertificateProvider a1Provider = new Pkcs12CertificateProvider();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
X500Name name = new X500Name("CN=BENCHMARK TESTE:12345678900, O=ICP-Brasil Teste, C=BR");
Date notBefore = Date.from(Instant.now().minus(1, ChronoUnit.DAYS));
Date notAfter = Date.from(Instant.now().plus(30, ChronoUnit.DAYS));
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
name, BigInteger.ONE, notBefore, notAfter, name,
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
);
builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation));
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
X509CertificateHolder holder = builder.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(holder);
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null, null);
ks.setKeyEntry("bench-alias", keyPair.getPrivate(), "1234".toCharArray(), new Certificate[]{cert});
ByteArrayOutputStream baosKs = new ByteArrayOutputStream();
ks.store(baosKs, "1234".toCharArray());
a1Provider.loadFromBytes(baosKs.toByteArray(), "1234".toCharArray());
certManager.registerProvider(a1Provider);
System.out.println("OK (" + (System.currentTimeMillis() - tCertGenStart) + " ms)");
// 2. Mede Tempo de Enumeração de Certificados
System.out.print("[2/3] Medindo tempo de enumeração de certificados (discoverCertificates)... ");
long tDiscStart = System.nanoTime();
List<ManagedCertificate> certs = certManager.discoverCertificates();
long tDiscEnd = System.nanoTime();
double discMs = (tDiscEnd - tDiscStart) / 1_000_000.0;
System.out.printf("OK (%.2f ms | %d certificado(s) listado(s))\n", discMs, certs.size());
String certId = certs.get(0).id();
// 3. Prepara arquivos PDF de 100 KB, 5 MB e 50 MB
System.out.println("[3/3] Preparando e assinando documentos PDF com PAdES e selo visual...");
File pdf100Kb = preparePdf(new File(dir, "doc_100kb.pdf"), 100 * 1024);
File pdf5Mb = preparePdf(new File(dir, "doc_5mb.pdf"), 5 * 1024 * 1024);
File pdf50Mb = preparePdf(new File(dir, "doc_50mb.pdf"), 50 * 1024 * 1024);
PdfSigningService pdfService = new PdfSigningService(certManager);
PdfSignatureOptions options = PdfSignatureOptions.builder(certId)
.position(PdfSealPosition.BOTTOM_RIGHT)
.visibleSeal(true)
.reason("Homologacao de Performance")
.location("Brasilia, DF")
.pinOrPassword("1234".toCharArray())
.build();
// Warmup JIT (PDF pequeno)
pdfService.signPdf(Files.readAllBytes(pdf100Kb.toPath()), options);
// Medição 100 KB
long t100Start = System.nanoTime();
byte[] signed100 = pdfService.signPdf(Files.readAllBytes(pdf100Kb.toPath()), options);
long t100End = System.nanoTime();
double ms100 = (t100End - t100Start) / 1_000_000.0;
// Medição 5 MB
long t5mStart = System.nanoTime();
byte[] signed5m = pdfService.signPdf(Files.readAllBytes(pdf5Mb.toPath()), options);
long t5mEnd = System.nanoTime();
double ms5m = (t5mEnd - t5mStart) / 1_000_000.0;
// Medição 50 MB
long t50mStart = System.nanoTime();
byte[] signed50m = pdfService.signPdf(Files.readAllBytes(pdf50Mb.toPath()), options);
long t50mEnd = System.nanoTime();
double ms50m = (t50mEnd - t50mStart) / 1_000_000.0;
System.out.println();
System.out.println("==================== RESULTADOS DE LATÊNCIA PAdES ====================");
System.out.printf(" - Enumeração de Certificados: %.2f ms\n", discMs);
System.out.printf(" - Assinatura PAdES 100 KB: %.2f ms (saída: %d KB)\n", ms100, signed100.length / 1024);
System.out.printf(" - Assinatura PAdES 5 MB: %.2f ms (saída: %.2f MB)\n", ms5m, signed5m.length / (1024.0 * 1024.0));
System.out.printf(" - Assinatura PAdES 50 MB: %.2f ms (saída: %.2f MB)\n", ms50m, signed50m.length / (1024.0 * 1024.0));
System.out.println("=======================================================================");
}
private static File preparePdf(File file, int targetSizeBytes) throws Exception {
if (file.exists() && file.length() >= targetSizeBytes * 0.95) {
return file;
}
System.out.printf(" -> Gerando %s (~%d KB)... ", file.getName(), targetSizeBytes / 1024);
try (PDDocument doc = new PDDocument()) {
PDPage page = new PDPage();
doc.addPage(page);
try (PDPageContentStream cs = new PDPageContentStream(doc, page)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 14);
cs.newLineAtOffset(50, 750);
cs.showText("CyberCore Signer — Documento de Benchmark de Performance");
cs.endText();
}
// Para arquivos de 5MB e 50MB, adiciona páginas e streams para atingir a meta de tamanho
int approxPageSize = 2500;
int pagesNeeded = Math.max(1, targetSizeBytes / approxPageSize);
for (int p = 1; p < pagesNeeded && p < 1200; p++) {
PDPage extraPage = new PDPage();
doc.addPage(extraPage);
try (PDPageContentStream cs = new PDPageContentStream(doc, extraPage)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 10);
cs.newLineAtOffset(50, 700);
cs.showText("Pagina de teste de volume " + p + " com metadados para teste PAdES.");
cs.endText();
}
}
// Grava documento
try (FileOutputStream fos = new FileOutputStream(file)) {
doc.save(fos);
}
}
// Se ainda não atingiu o tamanho exato desejado (especialmente para 50MB), preenche comentários PDF (% padding)
long currentLen = file.length();
if (currentLen < targetSizeBytes) {
try (FileOutputStream fos = new FileOutputStream(file, true)) {
byte[] chunk = new byte[64 * 1024];
java.util.Arrays.fill(chunk, (byte) 'A');
chunk[0] = '%';
chunk[chunk.length - 1] = '\n';
while (file.length() < targetSizeBytes) {
long remaining = targetSizeBytes - file.length();
int toWrite = (int) Math.min(chunk.length, remaining);
fos.write(chunk, 0, toWrite);
}
}
}
System.out.printf("OK (%d KB)\n", file.length() / 1024);
return file;
}
}
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
# Script de medição de tempo de inicialização e memória RSS em idle do CyberCore Signer
PORT=21325
JAR_FILE="modules/cybercore-app/target/cybercore-signer.jar"
JAVA_BIN="${JAVA_HOME:-/home/claudecio/.jdks/jdk-25.0.4.1+1}/bin/java"
if [ ! -f "$JAR_FILE" ]; then
echo "Erro: JAR $JAR_FILE não encontrado. Execute ./mvnw package primeiro."
exit 1
fi
# Garante que a porta esteja livre antes de iniciar
fuser -k "${PORT}/tcp" 2>/dev/null || true
sleep 1
# Timestamp inicial em milissegundos
START_TIME=$(date +%s%3N)
# Inicia o agente em modo headless
"$JAVA_BIN" -jar "$JAR_FILE" --headless --port "$PORT" > /dev/null 2>&1 &
APP_PID=$!
# Aguarda até que o endpoint CCSP /status responda HTTP 200
MAX_WAIT_SECONDS=15
READY=0
while true; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${PORT}/ccsp/v1/status" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
READY=1
break
fi
CURRENT_TIME=$(date +%s%3N)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [ "$ELAPSED" -gt $((MAX_WAIT_SECONDS * 1000)) ]; then
echo "Timeout aguardando inicialização do servidor CCSP."
kill -9 "$APP_PID" 2>/dev/null || true
exit 1
fi
sleep 0.05
done
END_TIME=$(date +%s%3N)
STARTUP_MS=$((END_TIME - START_TIME))
# Aguarda 2 segundos para estabilização de memória em repouso (idle)
sleep 2
# Mede RSS via ps (em KB)
RSS_KB=$(ps -o rss= -p "$APP_PID" | tr -d ' ' || echo "0")
RSS_MB=$(awk "BEGIN {printf \"%.2f\", $RSS_KB / 1024}")
# Encerra o processo limpo
kill "$APP_PID" 2>/dev/null || true
wait "$APP_PID" 2>/dev/null || true
echo "STARTUP_MS=${STARTUP_MS}"
echo "RSS_KB=${RSS_KB}"
echo "RSS_MB=${RSS_MB}"
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
# Script mestre de execução e consolidação de benchmarks de performance do CyberCore Signer
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"
JAVA_BIN="${JAVA_HOME:-/home/claudecio/.jdks/jdk-25.0.4.1+1}/bin/java"
JAVAC_BIN="${JAVA_HOME:-/home/claudecio/.jdks/jdk-25.0.4.1+1}/bin/javac"
JAR_FILE="modules/cybercore-app/target/cybercore-signer.jar"
echo "======================================================================="
echo " CYBERCORE SIGNER — SUÍTE DE MEDIÇÃO DE PERFORMANCE"
echo "======================================================================="
echo "Ambiente: $(uname -s) $(uname -m)"
echo "Java: $("$JAVA_BIN" -version 2>&1 | head -n 1)"
echo
# 1. Medições de Tempo de Inicialização e RSS (Média de 3 execuções)
echo "-----------------------------------------------------------------------"
echo "[1/2] Medindo Tempo de Inicialização do Servidor CCSP e Memória RSS..."
echo "-----------------------------------------------------------------------"
STARTUP_SUM=0
RSS_SUM=0
RUNS=3
for i in $(seq 1 $RUNS); do
echo -n " -> Execução $i de $RUNS: "
OUT=$(./scripts/bench/bench_startup_rss.sh)
S_MS=$(echo "$OUT" | grep "STARTUP_MS=" | cut -d'=' -f2)
R_MB=$(echo "$OUT" | grep "RSS_MB=" | cut -d'=' -f2)
echo "Startup = ${S_MS} ms | RSS = ${R_MB} MB"
STARTUP_SUM=$((STARTUP_SUM + S_MS))
RSS_SUM=$(awk "BEGIN {print $RSS_SUM + $R_MB}")
sleep 1
done
AVG_STARTUP=$((STARTUP_SUM / RUNS))
AVG_RSS=$(awk "BEGIN {printf \"%.2f\", $RSS_SUM / $RUNS}")
echo
echo "=> Média de Inicialização CCSP: ${AVG_STARTUP} ms"
echo "=> Média de Memória (RSS) em Idle: ${AVG_RSS} MB"
echo
# 2. Compilação e Execução do BenchmarkRunner (Certificados e PAdES)
echo "-----------------------------------------------------------------------"
echo "[2/2] Medindo Enumeração de Certificados e Latência PAdES..."
echo "-----------------------------------------------------------------------"
mkdir -p target/bench-classes
"$JAVAC_BIN" -cp "$JAR_FILE" -d target/bench-classes scripts/bench/BenchmarkRunner.java
"$JAVA_BIN" -cp "target/bench-classes:$JAR_FILE" scripts.bench.BenchmarkRunner