4

I'm working on optimizing some Emacs Lisp code that I've written. Part of this code needs to move elements from one vector to another. Currently, I have this code:

(defun copy-elements (source source-begin source-end
                      destination destination-begin)
  "Copy elements from SOURCE to DESTINATION.

We start at SOURCE[SOURCE-BEGIN], and the last element copied is
SOURCE[SOURCE-END - 1].

These elements are copied beginning at DESTINATION[DESTINATION-BEGIN].
This means DESTINATION must have enough space.

This method returns DESTINATION, and does not modify SOURCE.

If SOURCE and DESTINATION are the same, overlapping could cause issues."
  (dotimes (i (- source-end source-begin))
    (setf (seq-elt destination (+ i destination-begin))
          (seq-elt source (+ source-begin i))))
  destination)

This works fine:

ELISP> (let ((v1 [1 2 3]) (v2 [4 5 6])) (copy-elements v1 0 1 v2 2) (list v1 v2))
([1 2 3]
 [4 5 1])

But it's not as efficient as it could be. Even though we know we're working on consecutive elements, we move each one separately.

Is there a way to move or copy these elements? I don't need to share structure, so copying them would be perfectly fine if that's more efficient.

In fns.c, the function copy-sequence uses the C function memcpy to move things around vectors, but I don't see a way to do that myself.

What can I do to speed up moving consecutive elements from one vector to another?

  • 3
    The seq.el API is generalised over all sequence types so will always be less efficient than vector-specific operations, so prefer aref and aset over setf-ing seq-elt. In idiomatic Elisp, there is usually no need for memcpy-like operations. Instead, you can efficiently create a copy of a subvector using the built-in function substring, or efficiently join subvectors using the built-in function vconcat. – Basil Jan 29 at 8:40
  • 4
    substring with a vector argument is pretty hidden in the documentation if you're trying to learn about vectors. They're both array types, but even so... that's pretty obtuse even by lisp standards. – phils Jan 29 at 9:14
  • @Basil Please add that as an answer. – Tobias Jan 29 at 15:26
2

I don't think there's such a thing and using memcpy on incompatible types is risky, so I went for the dumb solution in my last emulator project:

(defun chip8--memcpy (dest dest-offset src src-offset n)
  (dotimes (i n)
    (aset dest (+ dest-offset i) (aref src (+ src-offset i)))))

It works fine because copying memory isn't the bottleneck in my program, drawing pixels is.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.