Чтобы по простому... Не передавайте значений. Передавайте ссылки. Тогда точно не будет копирования.
В $ret вернули или скаляр или ссылку на хеш / массив (ссылка вообще так-же является скаляром, но размером ссылки можно пренебречь).
Его уже можно передавать дальше. Ничего страшного не будет.
Дальше вы создаете @res. Это массив. При return @res он будет скопирован, а потом изначальный массив будет удален (тут могу ошибаться, так как сюда прямо просится оптимизация, возможно она уже есть). Когда вы будете принимать ответ вы напишите или:
@result = your_sub_with_foreach() или ($res1, $res2 ...) = your_sub();
В первом случае так-же будет копирование (уже 2е). Во втором - массив будет преобразован в список(немного виртуальный тип данных, который существует только в момент выполнения оператора) и из него будут выбраны первые n значений которые будут помещены в $res1, $res2 и т.д.
Для ухода от таких проблем достаточно заменить ваш return на return \@res в этом случае функция вернет ссылку на массив и принимающая сторона будет выглядеть так: my $res = your_sub(); $res->[0] - первый элемент массива. В этом случае копирований не будет (вернее будет, но только ссылок, что незначительно).