memmove - AugustinLopez/libft GitHub Wiki

Introduction

From The Open Group Base Specifications issue 7, 2018:

#include <string.h>

void *memmove(void *s1, const void *s2, size_t n);
  • The memmove() function shall copy n bytes from the object pointed to by s2 into the object pointed to by s1.
  • Copying takes place as if the n bytes from the object pointed to by s2 are first copied into a temporary array of n bytes that does not overlap the objects pointed to by s1 and s2, and then the n bytes from the temporary array are copied into the object pointed to by s1.
  • The memmove() function shall return s1; no return value is reserved to indicate an error.
  • No errors are defined.

Relevant wiki: memcpy.

Implementation

Relevant concepts: Operator precedence, type casting, const keyword.

Basic implementation

#include "libft.h"
#include <string.h>

void	*ft_memmove(void *s1, const void *s2, size_t n)
{
	unsigned char		*cs1;
	const unsigned char	*cs2;

	if (s1 < s2)
		return (ft_memcpy(s1, s2, n));
	if (!n || (s1 == s2))
		return (s1);
	cs1 = (unsigned char *)s1;
	cs2 = (const unsigned char *)s2;
	while (n--)
		cs1[n] = cs2[n];
	return (s1);
}

Improved implementation

For more information, please refer to the Optimization Concepts page.

#include "libft.h"
#include <string.h>
#include <stdint.h>

void	*ft_memmove(void *s1, const void *s2, size_t n)
{
	unsigned char		*cs1;
	const unsigned char	*cs2;
	uint64_t			*lls1;
	const uint64_t		*lls2;

	if (s1 < s2)
		return (ft_memcpy(s1, s2, n));
	else if (!n || (s1 == s2))
		return (s1);
	cs1 = (unsigned char *)s1 + n - 1;
	cs2 = (const unsigned char *)s2 + n - 1;
	if ((n >= 15) && (((uintptr_t)s1 & 0x7) == ((uintptr_t)s2 & 0x7)))
	{
		while (((uintptr_t)cs1 & 0x7))
		{
			*cs1-- = *cs2--;
			--n;
		}
		*cs1 = *cs2;
		lls1 = (uint64_t *)cs1 - 1;
		lls2 = (const uint64_t *)cs2 - 1;
		while (n >= 8)
		{
			*lls1-- = *lls2--;
			n -= 8;
		}
		if (n)
		{
			cs1 = (unsigned char *)lls1 - 1;
			cs2 = (const unsigned char *)lls2 - 1;
		}
	}
	while (n--)
		*cs1-- = *cs2--;
	return (s1);
}

Speed tests

  • Results are a percentage of the performance against a glibc memmove [ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27].
  • Compiler is gcc [(Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0].
  • for each size the test runtime is 5 seconds.
  • Memory is aligned.

Basic implementation

Size\flag -O3 -fno-builtin -O2 -Os (none)
1 99% 99% 101% 100%
10 98% 99% 99% 98%
100 98% 98% 95% 84%
1K 78% 78% 75% 35%
10K 31% 30% 26% 6%
100K 14% 14% 12% 2%
1M 12% 13% 12% 2%

Improved implementation

Size\flag -O3 -fno-builtin -O2 -Os (none)
1 99% 100% 99% 98%
10 98% 97% 99% 97%
100 98% 99% 100% 96%
1K 96% 97% 95% 81%
10K 93% 84% 82% 38%
100K 100% 83% 83% 19%
1M 83% 75% 75% 19%

Test function

# include <time.h>
# include <string.h>
# include <stdlib.h>

size_t	memmove_speed_test(int len, int second, void *(mem)(void *, const void *, size_t))
{
	clock_t			chrono = 0;
	clock_t			start = 0;
	size_t			i = 0;
	void			*dst;
	void			*src1;
	void			*src2;
	void			*tmp;

	dst = malloc(len);
	src1 = malloc(len);
	src2 = malloc(len);
	if (!dst || !src1 || !src2)
		exit(1);
	if (dst < src1)
	{
		tmp = dst;
		dst = src1;
		src1 = tmp;
	}
	if (dst < src2)
	{
		tmp = dst;
		dst = src2;
		src2 = tmp;
	}
	memset(src1, 0xff, len);
	memset(src2, 0, len);
	memset(dst, 0, len);
	start = clock();
	while (chrono < second * CLOCKS_PER_SEC)
	{
		if (i % 2)
			mem(dst, src1, len);
		else
			mem(dst, src2, len);
		i++;
		chrono = clock() - start;
	}
	free(dst);
	free(src1);
	free(src2);
	return (i);
}

Other implementations

⚠️ **GitHub.com Fallback** ⚠️