Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vectorizing operations in a contained class without breaking class structure

My current project is split into multiple classes that correctly represent the way we think of the entities in the system. However, this is taking a performance price, so I'm trying to come up with a way of improving performance while maintaining the current class structure we have.

I think an example will best illustrate the problem: Let's say I have an Order class that contains multiple Items. A naive implementation for shipping an Order is shipping all of its Items:

class Order 
{
    private List<Item> m_items;

    public void Ship()
    {
        foreach (Item item in m_items)
        {
            item.Ship();
        }
    }
}

Shipping an Item is a multi-stage process:

class Item
{
    public void Ship()
    {
        var receipt = m_boat.ShipToDestination(this);
        ProcessReceipt(receipt);
    }
}

As it turns out, shipping the Item on a Boat takes a long time. Luckily, the Boat can ship multiple Items concurrently (List<Receipt> Boat.ShipToDestination(List<Item> items)). However, to use this method I will have to rewrite Order.Ship() as follows:

class Order 
{
    public void Ship()
    {
        var receipts = m_boat.ShipToDestination(m_items);
        foreach (Receipt receipt in receipts)
        {
            ProcessReceipt(receipt);
        }
    }
}

This means that ProcessReceipt is now part of Order (rather than Item), and also that an Item no longer takes care of all of its shipping logistics.

In my real code there are many methods with this problem, so using this solution effectively means that most of the Item logic will move into the Order class. However, this seems like the wrong place to have this logic - it really should be in Item.

I considered using multiple threads (e.g. using a Task per Item) but this seems too wasteful, especially since an Order can have dozens of Items.

Another approach I considered is creating a QueuedBoat with a QueueForShipping(Item item) method and a ShipQueuedItems() method. Now Order can call ShipQueuedItems while Item can call QueueForShipping. The problem with this approach is that in my real code, the Receipt is returned only after the Item was shipped, so the code would have to look like this:

class Order 
{
    private List<Item> m_items;

    public void Ship()
    {
        foreach (Item item in m_items)
        {
            item.Ship();
        }

        m_queuedBoat.ShipQueuedItems();

        foreach (Item item in m_items)
        {
            item.FinalizeShipping();
        }
    }
}


class Item
{
    public void Ship()
    {
        m_queuedBoat.QueueForShipping(this);
    }

    public void FinalizeShipping()
    {
        ProcessReceipt(m_queuedBoat.GetReceiptForItem(this));
    }
}

This feels like it's in the right direction (since Order knows how many Items it has, it must be the component that knows when everything is on the Boat), but it broke Ship into two stages. In my real code I have multiple stages (each can be vectorized separately), so the method would be broken into 3-4 pieces, which is not a pretty sight.

Can someone suggest a way of vectorizing Item.Ship() while keeping the business logic inside the Item class, or will I have to give up on the Order/Item separation in order to take advantage of vectorization?

Edit:

Another approach could be this:

class Order 
{
    private List<Item> m_items;

    public void Ship()
    {
        for (int i = 0; i < m_items.Count; ++i)
        {
            bool isLast = (i == (m_items.Count - 1));
            item.Ship(isLast);
        }
    }
}

class Item
{
    public void Ship(bool isLast)
    {
        m_queuedBoat.QueueForShipping(this, FinalizeShipping);
        if (isLast) m_queuedBoat.ShipQueuedItems();
    }

    private void FinalizeShipping(Receipt receipt)
    {
        ProcessReceipt(receipt);
    }
}

Where the QueuedBoat class invokes a callback for each item with its Receipt.

like image 555
telewin Avatar asked Aug 20 '26 12:08

telewin


1 Answers

Well, anyway Ship is a kind of asynchronous operation: there must run several at once. So has to be two-stage: PrepareShipping (void) and PerformShipping (waits for the actual shipping to finish and returns processes its receipt).

I would so something like that:

class Item
{
    public void PrepareShiping()
    {
        m_queuedBoat.QueueForShipping(this);
    }

    public void PerformShipping()
    {
        m_queuedBoat.StartActualShippingIfNotAlreadyStarted();
        m_queuedBoat.WaitForShippingEnd();
        ProcessReceipt(m_queuedBoat.GetReceiptForItem(this));
    }
}

So the Order can be something like that:

class Order 
{
    public void PrepareShipping()
    {
        foreach (Item item in m_items)
            item.PrepareShiping();
    }

    public void PerformShipping()
    {
        foreach (Item item in m_items)
            item.PerformShipping();
    }

    // and for those who don't need asynchronity
    public void Ship()
    {
        PrepareShipping();
        PerformShipping();
    }
}

The program can do additional things while shipping is in progress, that is, between Order.PrepareShipping() and Order.PerformShipping() (for example, ship out other orders as well, maybe on the same boat).

like image 72
Vlad Avatar answered Aug 23 '26 02:08

Vlad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!