# Implementing a 2d point with pairs vs. flvectors vs. structs vs. complex, a primitive benchmark

**URL:** <https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274>\
**Category:** Show & Tell\
**Tags:** performance, discussion\
**Created:** [October 27, 2024, 10:25pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274 "2024-10-27T22:25:56Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![williewillus](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/williewillus/32/533_2.png) [@williewillus](https://racket.discourse.group/u/williewillus)\
**Post date:** [October 27, 2024, 10:25pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/1 "2024-10-27T22:25:56Z")

</div>

Recently had a thought experiment since I'm making a 2d game, which is heavy on numerical manipulation of 2d points.  
[I'm using Chez Scheme for the game, but that's not too relevant to this post].

The question was, there's quite a few ways we can represent 2d points. Which way is generally the "most efficient"?

The contenders are:

- Good old cons pairs, with `x` in the `car` and `y` in the `cdr`
- 2-long flvectors, with `x` as the first element and `y` in the second
- Structs/records, with `x` in the first slot and `y` in the second
- Complex numbers, with `x` in the real part and `y` in the imaginary part

I made a microbenchmark:

> **Expand**
>
> ```scheme
> #lang racket/base
> 
> (require racket/flonum)
> 
> (define (deal)
> ;; assumption: random's performance is mostly uniform
> (exact->inexact (random -5000 5000)))
> (define test-count 5000000)
> 
> (define (pair-add a b)
> (cons (fl+ (car a) (car b)) (fl+ (cdr a) (cdr b))))
> (define (pair-unit p)
> (define x (car p))
> (define y (cdr p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (cons (fl/ x norm) (fl/ y norm)))
> (define (pair-scale p scalar)
> (cons (fl* (car p) scalar) (fl* (cdr p) scalar)))
> (define (test-pair)
> (define points
> (for/vector ([i (in-range test-count)])
> (cons (deal) (deal))))
> (define toadd (cons (deal) (deal)))
> (define adds (for/vector ([p (in-vector points)])
> (pair-add p toadd)))
> (define norms (for/vector ([p (in-vector points)])
> (pair-unit p)))
> (define scalar (deal))
> (define scales (for/vector ([p (in-vector points)])
> (pair-scale p scalar)))
> (list (vector-length adds) (vector-length norms) (vector-length scales)))
> 
> (define (flvector-add a b)
> (flvector (fl+ (flvector-ref a 0) (flvector-ref b 0))
> (fl+ (flvector-ref a 1) (flvector-ref b 1))))
> (define (flvector-unit p)
> (define x (flvector-ref p 0))
> (define y (flvector-ref p 1))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (flvector (fl/ x norm) (fl/ y norm)))
> (define (flvector-scale p scalar)
> (flvector (fl* (flvector-ref p 0) scalar) (fl* (flvector-ref p 1) scalar)))
> (define (test-flvector)
> (define points
> (for/vector ([i (in-range test-count)])
> (flvector (deal) (deal))))
> (define toadd (flvector (deal) (deal)))
> (define adds (for/vector ([p (in-vector points)])
> (flvector-add p toadd)))
> (define norms (for/vector ([p (in-vector points)])
> (flvector-unit p)))
> (define scalar (deal))
> (define scales (for/vector ([p (in-vector points)])
> (flvector-scale p scalar)))
> (list (vector-length adds) (vector-length norms) (vector-length scales)))
> 
> (struct vec2 (x y))
> (define (struct-add a b)
> (vec2 (fl+ (vec2-x a) (vec2-y b))
> (fl+ (vec2-y a) (vec2-y b))))
> (define (struct-unit p)
> (define x (vec2-x p))
> (define y (vec2-y p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (vec2 (fl/ x norm) (fl/ y norm)))
> (define (struct-scale p scalar)
> (vec2 (fl* (vec2-x p) scalar) (fl* (vec2-y p) scalar)))
> (define (test-struct)
> (define points
> (for/vector ([i (in-range test-count)])
> (vec2 (deal) (deal))))
> (define toadd (vec2 (deal) (deal)))
> (define adds (for/vector ([p (in-vector points)])
> (struct-add p toadd)))
> (define norms (for/vector ([p (in-vector points)])
> (struct-unit p)))
> (define scalar (deal))
> (define scales (for/vector ([p (in-vector points)])
> (struct-scale p scalar)))
> (list (vector-length adds) (vector-length norms) (vector-length scales)))
> 
> (define (complex-unit p)
> (define x (flreal-part p))
> (define y (flimag-part p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (make-flrectangular (fl/ x norm) (fl/ y norm)))
> (define (complex-scale p scalar)
> (make-flrectangular (fl* (flreal-part p) scalar) (fl* (flimag-part p) scalar)))
> (define (test-complex)
> (define points
> (for/vector ([i (in-range test-count)])
> (make-flrectangular (deal) (deal))))
> (define toadd (make-flrectangular (deal) (deal)))
> (define adds (for/vector ([p (in-vector points)])
> (+ p toadd)))
> (define norms (for/vector ([p (in-vector points)])
> (complex-unit p)))
> (define scalar (deal))
> (define scales (for/vector ([p (in-vector points)])
> (complex-scale p scalar)))
> (list (vector-length adds) (vector-length norms) (vector-length scales)))
> 
> (define (main)
> (display "Pairs:")
> (collect-garbage 'major)
> (time (displayln (test-pair)))
> 
> (display "flvectors:")
> (collect-garbage 'major)
> (time (displayln (test-flvector)))
> 
> (display "structs:")
> (collect-garbage 'major)
> (time (displayln (test-struct)))
> 
> (display "complex:")
> (collect-garbage 'major)
> (time (displayln (test-complex))))
> 
> (module* main #f
> (main))
> 
> ```

For each approach, I generate 5 million points, then exercise the approach as follows:

1. Randomly generate another point (once) and add it to the 5 million points
2. Normalize each point to have magnitude 1
3. Scale each point by a scalar

Here's the results I saw:

```scheme
Pairs:
cpu time: 5390 real time: 5396 gc time: 4113
flvectors:
cpu time: 3272 real time: 3275 gc time: 1969
structs:
cpu time: 6421 real time: 6428 gc time: 5085
complex:
cpu time: 3697 real time: 3700 gc time: 2223

```

Not scientifically rigorous benchmarking, though the general trend appears reproducible across multiple runs. It seems flvectors are the most competitive, even though reading from them may potentially allocate every single time (in this microbenchmark, it's unlikely to allocate since the elements are immediately processed and put into another flvector).  
Complexes are surprisingly competitive as well, likely for the same reasons, with cons cells and records coming in quite a bit further behind.

Just putting this out there as a curiosity. The improvement would be 1) use a real benchmarking library to do more iterations and 2) Test the scenario where reads from the structure are not immediately consumed locally and are passed elsewhere, possibly forcing flvectors and complexes to allocate.

---

<div class="post-metadata">

**Author:** ![williewillus](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/williewillus/32/533_2.png) [@williewillus](https://racket.discourse.group/u/williewillus)\
**Post date:** [October 27, 2024, 10:30pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/2 "2024-10-27T22:30:00Z")

</div>

Another thing to test would be the memory overhead of each approach.

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [October 27, 2024, 11:46pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/3 "2024-10-27T23:46:27Z")

</div>

I was going to ask if you'd measured memory usage too. My suspicion is that flvectors will use the most memory - the vectors have to include their length after all in addition to their payload, unlike the other approaches.

(Historically I use complex numbers for Cartesian coordinates)

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [October 28, 2024, 2:19am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/4 "2024-10-28T02:19:59Z")

</div>

> [@williewillus](#):
>
> It seems flvectors are the most competitive, even though reading from them may potentially allocate every single time (in this microbenchmark, it's unlikely to allocate since the elements are immediately processed and put into another flvector).

My guess is that with flvectors, you're helping the compiler unbox as much as possible. When you have code like:

> [@williewillus](#):
>
> ```scheme
> (define (flvector-unit p)
> (define x (flvector-ref p 0))
> (define y (flvector-ref p 1))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (flvector (fl/ x norm) (fl/ y norm)))
> 
> ```

The compiler knows that the result of `flvector-ref` will always be a flonum, so it should be able to avoid (un)boxing any intermediate values. (Indeed, since the values come from and go back into flvectors, they should never be boxed at all.)

In contrast, with:

> [@williewillus](#):
>
> ```scheme
> (define (pair-unit p)
> (define x (car p))
> (define y (cdr p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (cons (fl/ x norm) (fl/ y norm)))
> 
> ```

The contents of the pair can be anything, so some internal `flonum?` check has to be inserted before unboxing, and there has to _be_ boxing and unboxing to put the flonums in and out of the pair, so that's an extra level of indirection.

> [@williewillus](#):
>
> [I'm using Chez Scheme for the game, but that's not too relevant to this post].

You might be interested in [`define-record-type`](https://cisco.github.io/ChezScheme/csug10.0/objects.html#./objects:h17):

```scheme
(define-record fl2d ([immutable double-float x] 
                     [immutable double-float y]))

```

I haven't tested, but it might combine the unboxing benefits of flvectors with explicit statically-known fields.

---

<div class="post-metadata">

**Author:** ![spdegabrielle](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/spdegabrielle/32/95_2.png) [@spdegabrielle](https://racket.discourse.group/u/spdegabrielle)\
**Post date:** [October 28, 2024, 1:56pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/5 "2024-10-28T13:56:29Z")

</div>

Would Typed Racket have better performance?

My understanding is one of the reasons the Racket Math library uses Typed Racket is performance improvements.

Best reagards,  
Stephen 🐟

---

<div class="post-metadata">

**Author:** ![damien\_mattei](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/damien_mattei/32/2706_2.png) [@damien\_mattei](https://racket.discourse.group/u/damien_mattei)\
**Post date:** [October 28, 2024, 2:11pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/6 "2024-10-28T14:11:33Z")

</div>

i tested that changing racket/base in racket almost multiply the time by 2... i will try to reconsider using racket/base insteatd racket in all my programs when possible

update:

```scheme
../Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/bracket-apply.rkt:138:3: lambda: unbound identifier;
 also, no #%app syntax transformer is bound in the transformer phase in: lambda

../Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/assignment.rkt:125:3: lambda: unbound identifier;
 also, no #%app syntax transformer is bound in the transformer phase in: lambda

```

the failure comes fast 😀

as i see that having only one submodule with racket cause the penality on the whole time i will stop modifications here...

---

<div class="post-metadata">

**Author:** ![damien\_mattei](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/damien_mattei/32/2706_2.png) [@damien\_mattei](https://racket.discourse.group/u/damien_mattei)\
**Post date:** [October 28, 2024, 2:48pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/7 "2024-10-28T14:48:39Z")

</div>

TL;DR :

and i tested the same code with Scheme+ ,indexing vectors with [] and i almost double again the CPU time comparing to vectors 😀

```scheme
(define (vector-add+ a b)
  (vector {a[0] + b[0]}
	  {a[1] + b[1]}))
  
(define (vector-unit+ p)
  {x <- p[0]}
  {y <- p[1]}
  (define norm (sqrt {x * x + y * y}))
  (vector {x / norm} {y / norm}))

(define (vector-scale+ p scalar)
  (vector {p[0] * scalar} {p[1] * scalar}))

(define (test-vector+)
  (define points
    (for/vector ([i (in-range test-count)])
      (vector (deal) (deal))))
  (define toadd (vector (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (vector-add+ p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (vector-unit+ p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (vector-scale+ p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

```

```scheme
Pairs:(5000000 5000000 5000000)
cpu time: 16233 real time: 17345 gc time: 7246
flvectors:(5000000 5000000 5000000)
cpu time: 11679 real time: 12310 gc time: 5200
structs:(5000000 5000000 5000000)
cpu time: 17884 real time: 18721 gc time: 9300
complex:(5000000 5000000 5000000)
cpu time: 11717 real time: 12360 gc time: 4784
vectors:(5000000 5000000 5000000)
cpu time: 15912 real time: 16706 gc time: 8478
vectors+:(5000000 5000000 5000000)
cpu time: 26124 real time: 27735 gc time: 8990

```

Full code and long version:

```scheme
#lang reader SRFI-105

;;#lang racket/base

(require racket/flonum)

(require Scheme+)

(define (deal)
  ;; assumption: random's performance is mostly uniform
  (exact->inexact (random -5000 5000)))
(define test-count 5000000)

(define (pair-add a b)
  (cons (fl+ (car a) (car b)) (fl+ (cdr a) (cdr b))))
(define (pair-unit p)
  (define x (car p))
  (define y (cdr p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (cons (fl/ x norm) (fl/ y norm)))
(define (pair-scale p scalar)
  (cons (fl* (car p) scalar) (fl* (cdr p) scalar)))
(define (test-pair)
  (define points
    (for/vector ([i (in-range test-count)])
      (cons (deal) (deal))))
  (define toadd (cons (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (pair-add p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (pair-unit p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                  (pair-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(define (flvector-add a b)
  (flvector (fl+ (flvector-ref a 0) (flvector-ref b 0))
            (fl+ (flvector-ref a 1) (flvector-ref b 1))))
(define (flvector-unit p)
  (define x (flvector-ref p 0))
  (define y (flvector-ref p 1))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (flvector (fl/ x norm) (fl/ y norm)))
(define (flvector-scale p scalar)
  (flvector (fl* (flvector-ref p 0) scalar) (fl* (flvector-ref p 1) scalar)))
(define (test-flvector)
  (define points
    (for/vector ([i (in-range test-count)])
      (flvector (deal) (deal))))
  (define toadd (flvector (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (flvector-add p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (flvector-unit p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (flvector-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(define (vector-add a b)
  (vector (+ (vector-ref a 0) (vector-ref b 0))
            (+ (vector-ref a 1) (vector-ref b 1))))
(define (vector-unit p)
  (define x (vector-ref p 0))
  (define y (vector-ref p 1))
  (define norm (sqrt (+ (* x x) (* y y))))
  (vector (/ x norm) (/ y norm)))
(define (vector-scale p scalar)
  (vector (* (vector-ref p 0) scalar) (* (vector-ref p 1) scalar)))
(define (test-vector)
  (define points
    (for/vector ([i (in-range test-count)])
      (vector (deal) (deal))))
  (define toadd (vector (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (vector-add p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (vector-unit p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (vector-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(define (vector-add+ a b)
  (vector {a[0] + b[0]}
	  {a[1] + b[1]}))
  
(define (vector-unit+ p)
  {x <- p[0]}
  {y <- p[1]}
  (define norm (sqrt {x * x + y * y}))
  (vector {x / norm} {y / norm}))

(define (vector-scale+ p scalar)
  (vector {p[0] * scalar} {p[1] * scalar}))

(define (test-vector+)
  (define points
    (for/vector ([i (in-range test-count)])
      (vector (deal) (deal))))
  (define toadd (vector (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (vector-add+ p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (vector-unit+ p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (vector-scale+ p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(struct vec2 (x y))
(define (struct-add a b)
  (vec2 (fl+ (vec2-x a) (vec2-y b))
             (fl+ (vec2-y a) (vec2-y b))))
(define (struct-unit p)
  (define x (vec2-x p))
  (define y (vec2-y p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (vec2 (fl/ x norm) (fl/ y norm)))
(define (struct-scale p scalar)
  (vec2 (fl* (vec2-x p) scalar) (fl* (vec2-y p) scalar)))
(define (test-struct)
  (define points
    (for/vector ([i (in-range test-count)])
      (vec2 (deal) (deal))))
  (define toadd (vec2 (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (struct-add p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (struct-unit p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (struct-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(define (complex-unit p)
  (define x (flreal-part p))
  (define y (flimag-part p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (make-flrectangular (fl/ x norm) (fl/ y norm)))
(define (complex-scale p scalar)
  (make-flrectangular (fl* (flreal-part p) scalar) (fl* (flimag-part p) scalar)))
(define (test-complex)
  (define points
    (for/vector ([i (in-range test-count)])
      (make-flrectangular (deal) (deal))))
  (define toadd (make-flrectangular (deal) (deal)))
  (define adds (for/vector ([p (in-vector points)])
                 (+ p toadd)))
  (define norms (for/vector ([p (in-vector points)])
                  (complex-unit p)))
  (define scalar (deal))
  (define scales (for/vector ([p (in-vector points)])
                   (complex-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))

(define (main)
  (display "Pairs:")
  (collect-garbage 'major)
  (time (displayln (test-pair)))

  (display "flvectors:")
  (collect-garbage 'major)
  (time (displayln (test-flvector)))

  (display "structs:")
  (collect-garbage 'major)
  (time (displayln (test-struct)))

  (display "complex:")
  (collect-garbage 'major)
  (time (displayln (test-complex)))

  (display "vectors:")
  (collect-garbage 'major)
  (time (displayln (test-vector)))

  (display "vectors+:")
  (collect-garbage 'major)
  (time (displayln (test-vector+)))

  )

(module* main #f
  (main))

```

```scheme
Welcome to DrRacket, version 8.14 [cs].
Language: reader SRFI-105, with debugging; memory limit: 8192 MB.
SRFI-105 Curly Infix parser for Racket Scheme by Damien MATTEI
(based on code from David A. Wheeler and Alan Manuel K. Gloria.)

Possibly skipping some header's lines containing space,tabs,new line,etc or comments.

SRFI-105.rkt : number of skipped lines (comments, spaces, directives,...) at header's beginning : 5

Parsed curly infix code result = 

(require racket/flonum)
(require Scheme+)
(define (deal) (exact->inexact (random -5000 5000)))
(define test-count 5000000)
(define (pair-add a b) (cons (fl+ (car a) (car b)) (fl+ (cdr a) (cdr b))))
(define (pair-unit p)
  (define x (car p))
  (define y (cdr p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (cons (fl/ x norm) (fl/ y norm)))
(define (pair-scale p scalar) (cons (fl* (car p) scalar) (fl* (cdr p) scalar)))
(define (test-pair)
  (define points (for/vector ((i (in-range test-count))) (cons (deal) (deal))))
  (define toadd (cons (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (pair-add p toadd)))
  (define norms (for/vector ((p (in-vector points))) (pair-unit p)))
  (define scalar (deal))
  (define scales (for/vector ((p (in-vector points))) (pair-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(define (flvector-add a b)
  (flvector
   (fl+ (flvector-ref a 0) (flvector-ref b 0))
   (fl+ (flvector-ref a 1) (flvector-ref b 1))))
(define (flvector-unit p)
  (define x (flvector-ref p 0))
  (define y (flvector-ref p 1))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (flvector (fl/ x norm) (fl/ y norm)))
(define (flvector-scale p scalar)
  (flvector (fl* (flvector-ref p 0) scalar) (fl* (flvector-ref p 1) scalar)))
(define (test-flvector)
  (define points
    (for/vector ((i (in-range test-count))) (flvector (deal) (deal))))
  (define toadd (flvector (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (flvector-add p toadd)))
  (define norms (for/vector ((p (in-vector points))) (flvector-unit p)))
  (define scalar (deal))
  (define scales
    (for/vector ((p (in-vector points))) (flvector-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(define (vector-add a b)
  (vector
   (+ (vector-ref a 0) (vector-ref b 0))
   (+ (vector-ref a 1) (vector-ref b 1))))
(define (vector-unit p)
  (define x (vector-ref p 0))
  (define y (vector-ref p 1))
  (define norm (sqrt (+ (* x x) (* y y))))
  (vector (/ x norm) (/ y norm)))
(define (vector-scale p scalar)
  (vector (* (vector-ref p 0) scalar) (* (vector-ref p 1) scalar)))
(define (test-vector)
  (define points
    (for/vector ((i (in-range test-count))) (vector (deal) (deal))))
  (define toadd (vector (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (vector-add p toadd)))
  (define norms (for/vector ((p (in-vector points))) (vector-unit p)))
  (define scalar (deal))
  (define scales (for/vector ((p (in-vector points))) (vector-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(define (vector-add+ a b)
  (vector
   ($nfx$ ($bracket-apply$ a 0) + ($bracket-apply$ b 0))
   ($nfx$ ($bracket-apply$ a 1) + ($bracket-apply$ b 1))))
(define (vector-unit+ p)
  ($nfx$ x <- ($bracket-apply$ p 0))
  ($nfx$ y <- ($bracket-apply$ p 1))
  (define norm (sqrt ($nfx$ x * x + y * y)))
  (vector ($nfx$ x / norm) ($nfx$ y / norm)))
(define (vector-scale+ p scalar)
  (vector
   ($nfx$ ($bracket-apply$ p 0) * scalar)
   ($nfx$ ($bracket-apply$ p 1) * scalar)))
(define (test-vector+)
  (define points
    (for/vector ((i (in-range test-count))) (vector (deal) (deal))))
  (define toadd (vector (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (vector-add+ p toadd)))
  (define norms (for/vector ((p (in-vector points))) (vector-unit+ p)))
  (define scalar (deal))
  (define scales
    (for/vector ((p (in-vector points))) (vector-scale+ p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(struct vec2 (x y))
(define (struct-add a b)
  (vec2 (fl+ (vec2-x a) (vec2-y b)) (fl+ (vec2-y a) (vec2-y b))))
(define (struct-unit p)
  (define x (vec2-x p))
  (define y (vec2-y p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (vec2 (fl/ x norm) (fl/ y norm)))
(define (struct-scale p scalar)
  (vec2 (fl* (vec2-x p) scalar) (fl* (vec2-y p) scalar)))
(define (test-struct)
  (define points (for/vector ((i (in-range test-count))) (vec2 (deal) (deal))))
  (define toadd (vec2 (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (struct-add p toadd)))
  (define norms (for/vector ((p (in-vector points))) (struct-unit p)))
  (define scalar (deal))
  (define scales (for/vector ((p (in-vector points))) (struct-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(define (complex-unit p)
  (define x (flreal-part p))
  (define y (flimag-part p))
  (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
  (make-flrectangular (fl/ x norm) (fl/ y norm)))
(define (complex-scale p scalar)
  (make-flrectangular
   (fl* (flreal-part p) scalar)
   (fl* (flimag-part p) scalar)))
(define (test-complex)
  (define points
    (for/vector
     ((i (in-range test-count)))
     (make-flrectangular (deal) (deal))))
  (define toadd (make-flrectangular (deal) (deal)))
  (define adds (for/vector ((p (in-vector points))) (+ p toadd)))
  (define norms (for/vector ((p (in-vector points))) (complex-unit p)))
  (define scalar (deal))
  (define scales
    (for/vector ((p (in-vector points))) (complex-scale p scalar)))
  (list (vector-length adds) (vector-length norms) (vector-length scales)))
(define (main)
  (display "Pairs:")
  (collect-garbage 'major)
  (time (displayln (test-pair)))
  (display "flvectors:")
  (collect-garbage 'major)
  (time (displayln (test-flvector)))
  (display "structs:")
  (collect-garbage 'major)
  (time (displayln (test-struct)))
  (display "complex:")
  (collect-garbage 'major)
  (time (displayln (test-complex)))
  (display "vectors:")
  (collect-garbage 'major)
  (time (displayln (test-vector)))
  (display "vectors+:")
  (collect-garbage 'major)
  (time (displayln (test-vector+))))
(module* main #f (main))
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (($bracket-apply$ a 0) + ($br...>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ a 0)> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 +> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ b 0)>)
$nfx$ : parsed-args=.#<syntax (+ ($bracket-apply$ a 0) ($br...>

bracket-apply : #'parsed-args=.#<syntax (list 0)>

bracket-apply : #'parsed-args=.#<syntax (list 0)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (($bracket-apply$ a 1) + ($br...>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ a 1)> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 +> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ b 1)>)
$nfx$ : parsed-args=.#<syntax (+ ($bracket-apply$ a 1) ($br...>

bracket-apply : #'parsed-args=.#<syntax (list 1)>

bracket-apply : #'parsed-args=.#<syntax (list 1)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (x <- ($bracket-apply$ p 0))>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 x> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 <-> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ p 0)>)
$nfx$ : parsed-args=.#<syntax (<- x ($bracket-apply$ p 0))>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (y <- ($bracket-apply$ p 1))>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 y> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 <-> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ p 1)>)
$nfx$ : parsed-args=.#<syntax (<- y ($bracket-apply$ p 1))>

bracket-apply : #'parsed-args=.#<syntax (list 0)>

bracket-apply : #'parsed-args=.#<syntax (list 1)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (x * x + y * y)>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 x> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 *> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 x> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 +> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 y> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 *> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 y>)
$nfx$ : parsed-args=.#<syntax (+ (* x x) (* y y))>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (x / norm)>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 x> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 /> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 norm>)
$nfx$ : parsed-args=.#<syntax (/ x norm)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (y / norm)>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 y> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 /> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 norm>)
$nfx$ : parsed-args=.#<syntax (/ y norm)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (($bracket-apply$ p 0) * scalar)>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ p 0)> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 *> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 scalar>)
$nfx$ : parsed-args=.#<syntax (* ($bracket-apply$ p 0) scalar)>

bracket-apply : #'parsed-args=.#<syntax (list 0)>
$nfx$: #'(e1 op1 e2 op ...)=.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/nfx.rkt:65:69 (($bracket-apply$ p 1) * scalar)>
$nfx$: (syntax->list #'(e1 op1 e2 op ...))=(.#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 ($bracket-apply$ p 1)> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 *> .#<syntax:Users/mattei/Library/CloudStorage/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/benchmark+.rkt:1:6 scalar>)
$nfx$ : parsed-args=.#<syntax (* ($bracket-apply$ p 1) scalar)>

bracket-apply : #'parsed-args=.#<syntax (list 1)>
Pairs:(5000000 5000000 5000000)
cpu time: 16233 real time: 17345 gc time: 7246
flvectors:(5000000 5000000 5000000)
cpu time: 11679 real time: 12310 gc time: 5200
structs:(5000000 5000000 5000000)
cpu time: 17884 real time: 18721 gc time: 9300
complex:(5000000 5000000 5000000)
cpu time: 11717 real time: 12360 gc time: 4784
vectors:(5000000 5000000 5000000)
cpu time: 15912 real time: 16706 gc time: 8478
vectors+:(5000000 5000000 5000000)
cpu time: 26124 real time: 27735 gc time: 8990
> 

```

update:

+70% in time approximately ,that's bearable...

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [October 28, 2024, 3:03pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/8 "2024-10-28T15:03:51Z")

</div>

> [@damien\_mattei](#):
>
> `#%app syntax transformer is bound in the transformer phase in: lambda`

The reason is that `racket/base` doesn't import `racket/base` in phase 1,  
so you can fix it with `(require (for-syntax racket/base))`.

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [October 28, 2024, 3:06pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/9 "2024-10-28T15:06:55Z")

</div>

FWIW, when using `#lang racket/base` you'll need to `(require (for-syntax racket/base))`, which `#lang racket` does for you.

---

<div class="post-metadata">

**Author:** ![williewillus](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/williewillus/32/533_2.png) [@williewillus](https://racket.discourse.group/u/williewillus)\
**Post date:** [October 29, 2024, 2:27am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/10 "2024-10-29T02:27:18Z")

</div>

I repeated the benchmark in Chez Scheme for good measure. Similar results, except that complexes are relatively slower, and all of the benchmarks are much faster than Racket.

Run with `chez --program main.ss`

> **Expand**
>
> ```scheme
> #!chezscheme
> (import (chezscheme))
> (define rnd (make-pseudo-random-generator))
> (define (deal)
> ;; assumption: random's performance is mostly uniform
> (inexact (- (pseudo-random-generator-next! rnd 10000) 5000)))
> (define test-count 5000000)
> 
> (define (init-vec proc)
> (define ret (make-vector test-count 0))
> (do [(i 0 (add1 i))] [(>= i test-count)]
> (vector-set! ret i (proc)))
> ret)
> 
> (define (pair-add a b)
> (cons (fl+ (car a) (car b)) (fl+ (cdr a) (cdr b))))
> (define (pair-unit p)
> (define x (car p))
> (define y (cdr p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (cons (fl/ x norm) (fl/ y norm)))
> (define (pair-scale p scalar)
> (cons (fl* (car p) scalar) (fl* (cdr p) scalar)))
> (define (test-pair)
> (define points
> (init-vec (lambda () (cons (deal) (deal)))))
> (define toadd (cons (deal) (deal)))
> (define adds
> (vector-map (lambda (p) (pair-add p toadd)) points))
> (define norms
> (vector-map pair-unit points))
> (define scalar (deal))
> (define scales
> (vector-map (lambda (p) (pair-scale p scalar)) points))
> (list (vector-ref adds 0) (vector-ref norms 0) (vector-ref scales 0)))
> 
> (define (flvector-add a b)
> (flvector (fl+ (flvector-ref a 0) (flvector-ref b 0))
> (fl+ (flvector-ref a 1) (flvector-ref b 1))))
> (define (flvector-unit p)
> (define x (flvector-ref p 0))
> (define y (flvector-ref p 1))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (flvector (fl/ x norm) (fl/ y norm)))
> (define (flvector-scale p scalar)
> (flvector (fl* (flvector-ref p 0) scalar) (fl* (flvector-ref p 1) scalar)))
> (define (test-flvector)
> (define points
> (init-vec (lambda () (flvector (deal) (deal)))))
> (define toadd (flvector (deal) (deal)))
> (define adds
> (vector-map (lambda (p) (flvector-add p toadd)) points))
> (define norms
> (vector-map flvector-unit points))
> (define scalar (deal))
> (define scales
> (vector-map (lambda (p) (flvector-scale p scalar)) points))
> (list (vector-ref adds 0) (vector-ref norms 0) (vector-ref scales 0)))
> 
> (define-record-type vec2
> (fields x y))
> (define (struct-add a b)
> (make-vec2 (fl+ (vec2-x a) (vec2-y b))
> (fl+ (vec2-y a) (vec2-y b))))
> (define (struct-unit p)
> (define x (vec2-x p))
> (define y (vec2-y p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (make-vec2 (fl/ x norm) (fl/ y norm)))
> (define (struct-scale p scalar)
> (make-vec2 (fl* (vec2-x p) scalar) (fl* (vec2-y p) scalar)))
> (define (test-struct)
> (define points
> (init-vec (lambda () (make-vec2 (deal) (deal)))))
> (define toadd (make-vec2 (deal) (deal)))
> (define adds
> (vector-map (lambda (p) (struct-add p toadd)) points))
> (define norms
> (vector-map struct-unit points))
> (define scalar (deal))
> (define scales
> (vector-map (lambda (p) (struct-scale p scalar)) points))
> (list (vector-ref adds 0) (vector-ref norms 0) (vector-ref scales 0)))
> 
> (define-record lvec2 ((immutable double x) (immutable double y)))
> (define (lstruct-add a b)
> (make-lvec2 (fl+ (lvec2-x a) (lvec2-y b))
> (fl+ (lvec2-y a) (lvec2-y b))))
> (define (lstruct-unit p)
> (define x (lvec2-x p))
> (define y (lvec2-y p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (make-lvec2 (fl/ x norm) (fl/ y norm)))
> (define (lstruct-scale p scalar)
> (make-lvec2 (fl* (lvec2-x p) scalar) (fl* (lvec2-y p) scalar)))
> (define (test-lstruct)
> (define points
> (init-vec (lambda () (make-lvec2 (deal) (deal)))))
> (define toadd (make-lvec2 (deal) (deal)))
> (define adds
> (vector-map (lambda (p) (lstruct-add p toadd)) points))
> (define norms
> (vector-map lstruct-unit points))
> (define scalar (deal))
> (define scales
> (vector-map (lambda (p) (lstruct-scale p scalar)) points))
> (list (vector-ref adds 0) (vector-ref norms 0) (vector-ref scales 0)))
> 
> (define (complex-unit p)
> (define x (cfl-real-part p))
> (define y (cfl-imag-part p))
> (define norm (flsqrt (fl+ (fl* x x) (fl* y y))))
> (fl-make-rectangular (fl/ x norm) (fl/ y norm)))
> (define (complex-scale p scalar)
> (fl-make-rectangular (fl* (cfl-real-part p) scalar) (fl* (cfl-imag-part p) scalar)))
> (define (test-complex)
> (define points
> (init-vec (lambda () (fl-make-rectangular (deal) (deal)))))
> (define toadd (fl-make-rectangular (deal) (deal)))
> (define adds
> (vector-map (lambda (p) (+ p toadd)) points))
> (define norms
> (vector-map complex-unit points))
> (define scalar (deal))
> (define scales
> (vector-map (lambda (p) (complex-scale p scalar)) points))
> (list (vector-length adds) (vector-length norms) (vector-length scales)))
> 
> (define (main)
> (display "Pairs:")
> (collect 4 4)
> (time (display (test-pair)))
> 
> (display "flvectors:")
> (collect 4 4)
> (time (display (test-flvector)))
> 
> (display "structs:")
> (collect 4 4)
> (time (display (test-struct)))
> 
> (display "legacy structs:")
> (collect 4 4)
> (time (display (test-lstruct)))
> 
> (display "complex:")
> (collect 4 4)
> (time (display (test-complex))))
> 
> (main)
> 
> ```

Results:

```scheme
Pairs:((-5184.0 . 2842.0) (-0.999775853931432 . -0.021171723965606796) (8744375.0 . 185175.0))(time (display (test-pair)))
    30 collections
    0.928861577s elapsed cpu time, including 0.351799512s collecting
    0.932841115s elapsed real time, including 0.353317558s collecting
    1262891296 bytes allocated, including 982887776 bytes reclaimed
flvectors:(#vfl(-5858.0 604.0) #vfl(-0.7817194788876338 0.6236302240331575) #vfl(-950232.0 758064.0))(time (display (test-flvector)))
    40 collections
    0.807444927s elapsed cpu time, including 0.213906192s collecting
    0.811447900s elapsed real time, including 0.215408842s collecting
    1582897888 bytes allocated, including 1382896624 bytes reclaimed
structs:(#[#{vec2 ifg6mo42xdds2u625sqj3o7da-0} -3295.0 -253.0] #[#{vec2 ifg6mo42xdds2u625sqj3o7da-0} -0.9998308911299209 -0.018389919585158274] #[#{vec2 ifg6mo42xdds2u625sqj3o7da-0} -3557652.0 -65436.0])(time (display (test-struct)))
    40 collections
    1.124347418s elapsed cpu time, including 0.510183295s collecting
    1.130014981s elapsed real time, including 0.513124652s collecting
    1582913600 bytes allocated, including 1222884928 bytes reclaimed
legacy structs:(#[#{lvec2 ifg6mo42xdds2u625sqj3o7da-1} 4683.0 3760.0] #[#{lvec2 ifg6mo42xdds2u625sqj3o7da-1} 0.7906723880365014 0.612239475039511] #[#{lvec2 ifg6mo42xdds2u625sqj3o7da-1} -2065450.0 -1599335.0])(time (display (test-lstruct)))
    40 collections
    0.850581782s elapsed cpu time, including 0.310781624s collecting
    0.854529447s elapsed real time, including 0.312154663s collecting
    1102912016 bytes allocated, including 902884032 bytes reclaimed
complex:(5000000 5000000 5000000)(time (display (test-complex)))
    203 collections
    2.162937535s elapsed cpu time, including 1.601356059s collecting
    2.172449054s elapsed real time, including 1.608902293s collecting
    1867875456 bytes allocated, including 880127936 bytes reclaimed

```

---

<div class="post-metadata">

**Author:** ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)\
**Post date:** [October 29, 2024, 8:34pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/11 "2024-10-29T20:34:34Z")

</div>

I'm not sure yet all of the reasons why your Chez code is faster, but one big way to speed up the Racket code is to pass `#:length test-count` to all of the uses of `for/vector` so that they don't reallocate the vector repeatedly.

---

<div class="post-metadata">

**Author:** ![williewillus](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/williewillus/32/533_2.png) [@williewillus](https://racket.discourse.group/u/williewillus)\
**Post date:** [October 30, 2024, 12:22am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/12 "2024-10-30T00:22:38Z")

</div>

Racket results with #:length hints; a bit faster but still slower than pure Chez

```scheme
Pairs:(5000000 5000000 5000000)
cpu time: 4481 real time: 4486 gc time: 3474
flvectors:(5000000 5000000 5000000)
cpu time: 2635 real time: 2639 gc time: 1596
structs:(5000000 5000000 5000000)
cpu time: 5630 real time: 5636 gc time: 4570
complex:(5000000 5000000 5000000)
cpu time: 2949 real time: 2953 gc time: 1864

```

---

<div class="post-metadata">

**Author:** ![mflatt](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/mflatt/32/6_2.png) [@mflatt](https://racket.discourse.group/u/mflatt)\
**Post date:** [October 30, 2024, 1:12am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/13 "2024-10-30T01:12:03Z")

</div>

It's surprising that Racket is slower than Chez Scheme. ~~The main issue seems to be that schemify in Racket adds some `let`s in functions like `pair-unit` to force left-to-right evaluation, and cp0 is not able to eliminate them at the Chez Scheme level, which leads to extra boxing of intermediate flonum values.~~

~~I don't immediately know how to fix that, so it will require more investigation.~~

Edit: no, I was confused about unboxing.

---

<div class="post-metadata">

**Author:** ![mflatt](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/mflatt/32/6_2.png) [@mflatt](https://racket.discourse.group/u/mflatt)\
**Post date:** [October 30, 2024, 1:54am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/14 "2024-10-30T01:54:03Z")

</div>

The two things that I now think make the difference:

- `(random -5000 5000)` has to find the current random-number generator through a parameter on every call; changing it to `(random -5000 5000 rnd)` with `(define rnd (make-pseudo-random-generator))` saves most of the non-GC time
- Racket's GC heuristic causes it to send more time collecting in exchange for lower peak memory use: 1.5 GB peak for Racket versus 2.3 GB peak for Chez Scheme.

---

<div class="post-metadata">

**Author:** ![damien\_mattei](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/damien_mattei/32/2706_2.png) [@damien\_mattei](https://racket.discourse.group/u/damien_mattei)\
**Post date:** [October 30, 2024, 10:11am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/15 "2024-10-30T10:11:33Z")

</div>

@soegaard @benknoble

i changed from racket to racket/base in all the Scheme+ modules (33 modules) and i got a 15% speed up 🙂, with the vector+ test:

```scheme
Pairs:(5000000 5000000 5000000)
cpu time: 15231 real time: 15861 gc time: 6993
flvectors:(5000000 5000000 5000000)
cpu time: 12117 real time: 13728 gc time: 4894
structs:(5000000 5000000 5000000)
cpu time: 18013 real time: 19114 gc time: 9114
complex:(5000000 5000000 5000000)
cpu time: 12242 real time: 12782 gc time: 5120
vectors:(5000000 5000000 5000000)
cpu time: 16761 real time: 17469 gc time: 9229
vectors+:(5000000 5000000 5000000)
cpu time: 22887 real time: 24188 gc time: 9661

```

`cpu time: 22887` with racket/base versus  
`cpu time: 26124` with racket, in the future i will start all my code with racket/base instead of racket.

note: globally my MacOS M1 system seems on the knees 😥 ,on all the data types , this system has ran 3x times slower than what i read in the post, this has nothing to see with Racket. I should reboot , the system seems overloaded with no reason:

```scheme
mattei@mbp-touch-bar-1 ~ % uptime
11:30 up 4 days, 9:37, 9 users, load averages: 4,05 2,94 2,97

```

i should test the code on an intel PC or after reboot

---

<div class="post-metadata">

**Author:** ![gus-massa](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/gus-massa/32/507_2.png) [@gus-massa](https://racket.discourse.group/u/gus-massa)\
**Post date:** [October 31, 2024, 2:39pm UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/16 "2024-10-31T14:39:53Z")

</div>

In the complex number version in Chez Scheme, you may get a small improvement using `cfl+`, `cfl-`, `cfl*` and `cfl/`.

---

<div class="post-metadata">

**Author:** ![cadence](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/cadence/32/998_2.png) [@cadence](https://racket.discourse.group/u/cadence)\
**Post date:** [November 20, 2024, 2:47am UTC](https://racket.discourse.group/t/implementing-a-2d-point-with-pairs-vs-flvectors-vs-structs-vs-complex-a-primitive-benchmark/3274/17 "2024-11-20T02:47:57Z")

</div>

In eval-em-up, my game jam entry from last year, I store the X and Y positions as class fields. However, I do briefly convert them to complex numbers in order to calculate angles at a consistent magnitude.

> <https://github.com/cloudrac3r/eval-em-up/blob/90db15db72e5a9b0526cbee799e07e51c82f3251/main.rkt#L398>

Line-by-line explanation:

1. Calculate the difference in X and Y coordinates from the enemy ship to the player ship (e.g. `x = 3, y = -2`, and store those as a complex number named `difference-complex`.
2. Create a new complex number with the same angle as `difference-complex` but with a fixed magnitude.
3. Use this new complex number as the X and Y velocity of a new bullet.

This basically ensures that bullets fired from the enemy ship are always aimed directly towards the player ship, and that their overall velocity is consistent no matter how far apart the enemy and the player are.

I'm sure this could also be done with trig, but I was under time pressure and forgot trig.
