Tune the Code

Performance ist das Make-or-Break Kriterium eines Software-Systems. Performancedefizite verursachen in vielen Unternehmen jährliche Mehrausgaben in Millionenhöhe und bremsen Geschäftsprozesse sowie die Implementierung neuer Funktionen. Eine gezielte Performance-Optimierung kann in der Praxis bis zu 90% der laufenden Infrastrukturkosten von Softwarekomponenten einsparen.

 

Im Folgenden simulieren wir eine Massen-Inkasso-Verarbeitung eines Versicherungsunternehmens. Leider läuft diese im Moment noch sehr ineffizient. Optimiere die Laufzeit des folgenden Codes und qualifiziere dich für die Top 10! Der stark vereinfachte Ablauf besteht aus 2 Teilen:

1. Die eigentliche Inkasso-Verarbeitung erstellt eine Lastschrift für alle Verträge, die ein Bankkonto besitzen (d.h. per Lastschrift bezahlt werden).

2. Nach jeder erfolgreichen Inkassoverarbeitung wird ein Dokument für den Versicherungsnehmer mittels eines entkoppelten Output-/Print-Systems erstellt, um diesen über die bevorstehende Lastschrift zu informieren (Pre-Notification).

Hierbei werden die Daten aus einer simulierten Datenquelle mit fester Schnittstelle beschafft. Auf die Kernverarbeitung der Lastschrift sowie die Erstellung des Dokuments haben wir leider keinen Einfluss, wir können den Batch aber trotzdem deutlich optimieren!

Hinweise zum Vorgehen:

Bitte verändere nur die Datei „InkassoBatch“. Die optimierte Version muss natürlich auch korrekt sein.

Zur Vereinfachung der Entwicklung stehen dir 3 Konfigurationen mit unterschiedlichen Eingabemengen zur Verfügung, welche in „Main“ eingestellt werden können:

– Small: Laufzeit ca. 15 Sek.
– Medium: Laufzeit ca. 3 Min.
– Large: Laufzeit ca. 14 Min. -> diese wird für das Ranking herangezogen

Hier geht es zum Download

 


import datamodel.Bank;
import datamodel.BankAccount;
import datamodel.Contract;
import datamodel.PrintInput;
import dataprovider.BankAccountProvider;
import dataprovider.BankProvider;
import dataprovider.ContractProvider;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import system.InkassoSystem;
import system.PrintSystem;
import utils.Utils;

public class InkassoBatch {

    private int inkassoCount = 0;

    private int printRequestsCount = 0;
    private final MessageQueue<PrintInput> printSystemMessageQueue;

    public InkassoBatch() {
        this.printSystemMessageQueue = MessageQueue.getInstance(PrintInput.class);
    }

    public int getInkassoCount() {
        return inkassoCount;
    }

    public int getPrintRequestsCount() {
        return printRequestsCount;
    }

    public void runInkassoBatch() {

        for (final Contract contract : ContractProvider.getContracts()) {
            // find bank account for contract
            BankAccount bankAccountForContract = null;

            for (final BankAccount bankAccount : BankAccountProvider.getAccounts()) {
                if (contract.getBankAccountId() != null
                        && contract.getBankAccountId().intValue() == bankAccount.getId()) {
                    bankAccountForContract = bankAccount;
                }
            }

            if (bankAccountForContract != null) {
                inkassoCount++;
                final boolean inkassoSuccess = InkassoSystem.doInkasso(bankAccountForContract);
                if (inkassoCount % 1000 == 0) {
                    System.out.println("Current state: Completed " + inkassoCount + " Inkasso tasks");
                }

                if (inkassoSuccess) {
                    final Bank bankForBankAccount = BankProvider.getBankById(bankAccountForContract.getBankId());
                    if (bankForBankAccount == null) {
                        System.err.println("No bank found for account " + bankAccountForContract.getNumber());
                        return;
                    }

                    printRequestsCount++;
                    printSystemMessageQueue
                            .push(new PrintInput(bankForBankAccount.getId(), bankAccountForContract,
                                    contract.getId()));
                }
            }

        }
    }
}

class MessageQueue<T> {

    private static final Map<Class<?>, MessageQueue<?>> printSystemMessageQueue = new HashMap<>();

    public static <S> MessageQueue<S> getInstance(Class<S> clazz) {
        MessageQueue<S> queue = (MessageQueue<S>) printSystemMessageQueue.get(clazz);
        if (queue == null) {
            queue = new MessageQueue<>();
            printSystemMessageQueue.put(clazz, queue);
        }
        return queue;
    }

    private T[] buffer = null;
    private int size = 0;

    @SuppressWarnings("unchecked")
    public synchronized T pop() {
        if (size == 0) {
            return null;
        }
        final T obj = buffer[0];

        redim(--size, (Class<T>) obj.getClass());
        return obj;
    }

    @SuppressWarnings("unchecked")
    public synchronized void push(final T obj) {
        assert (obj != null);

        final int bufferLength = buffer == null ? 0 : buffer.length;
        if (size + 1 > bufferLength) {
            redim(size + 1, (Class<T>) obj.getClass());
        }
        buffer[size++] = obj;
    }

    /**
     * Re-Dimension of buffer-array to the given size and of given type. Data of
     * current buffer is being preserved. 
     * In case of increased buffer-size the data is left-aligned.
     * In case of decreased buffer-size the data is right-aligned.
     *
     * <pre>
     * buffer    -> method-call            -> buffer
     * ["A"]     -> redim(2, String.class) -> ["A", null]
     * ["A","B"] -> redim(1, String.class) -> ["B"]
     * </pre>
     *
     * @param newSize
     * @param genericClass
     */
    @SuppressWarnings("unchecked")
    private void redim(final int newSize, final Class<T> genericClass) {
        // create new buffer
        final T[] newBuffer = (T[]) Array.newInstance(genericClass, newSize);
        // copy data of old to new buffer if needed
        if (buffer != null && newSize > 0) {
            final int copyLength = Math.min(newSize, buffer.length);
            final int copyStartIndex = buffer.length - copyLength;
            System.arraycopy(buffer, copyStartIndex, newBuffer, 0, copyLength);
        }
        buffer = newBuffer;
    }
}

class Print implements Runnable {

    public static final int WAIT_ON_PRINT_QUEUE_MS = 1000;

    private static void printInkassoConfirmation(final int bankId, final BankAccount bankAccount,
            final int contractId) {
        final Optional<Contract> contractOptional = ContractProvider.getContracts().stream()
                .filter(c -> c.getId() == contractId).findFirst();
        if (!contractOptional.isPresent()) {
            System.err.println("Cannot find contract for account " + bankAccount.getId());
            return;
        }

        final Contract contract = contractOptional.get();
        final Bank bank = BankProvider.getBankById(bankId);
        final boolean printSuccess = PrintSystem.doPrintInkassoConfirmation(bank.getName(), bankAccount,
                contract.getPolicyHolderName());
        if (!printSuccess) {
            System.err.println("Could not print confirmation for " + bank.toString() + ", " + bankAccount + ", "
                    + contract.getPolicyHolderName());
        }

    }

    private final MessageQueue<PrintInput> messageQueue;

    private boolean run = true;

    public Print() {
        this.messageQueue = MessageQueue.getInstance(PrintInput.class);
    }

    private void processNextElement() {
        PrintInput element = messageQueue.pop();
        while (run || element != null) {
            if (element != null) {
                printInkassoConfirmation(element.getBankId(), element.getBankAccount(), element.getContractId());
            } else {
                System.out.println("Print system waiting " + WAIT_ON_PRINT_QUEUE_MS + " ms for next input");
                Utils.sleep(WAIT_ON_PRINT_QUEUE_MS);
            }
            element = messageQueue.pop();
        }
    }

    @Override
    public void run() {
        processNextElement();
    }

    public void stop() {
        run = false;
    }
}

 

Auf welche Laufzeit kommst du?

Schicke uns deine Lösung und erfahre, ob du unter die Top 10 kommst! Wenn du eine Antwort auf deine Nachricht möchtest, dann gib bitte deine E-Mail Adresse an.






Ich habe die Datenschutzhinweise gelesen und akzeptiert. Ich stimme zu,
dass meine Angaben und Daten zur Beantwortung meiner Anfrage
elektronisch erhoben und gespeichert werden.*
Hinweis: Sie können Ihre Einwilligung jederzeit widerrufen.

 

Tune the Code Champions

Pseudonym Differenz
kaeluka 461 ms
hoodaly 514 ms
gb 890 ms
k5_ 1.029 ms
doublem 1.090 ms