1

I have read some related questions, but nothing about speed comparison between memcpyand strncpy.

What do you recommend to keep track of a string content within a critical section?

  • avoid dynamic memory allocation
  • elegance of code and readable/understandable (few code lines)
  • fast processing (few instructions, prevent branch-miss)
  • able to be optimized by compiler (or having implementations already using optimized instructions)

I was thinking about functions:

  1. memcpy requires to calculate the min length (see snippet on coliru.stacked-crooked.com)

    void copy (char dst[20], const std::string& src)
    {
        if (src.size() < sizeof(dst)) {
            memcpy (dst, src.c_str(), src.size()+1);
        } else {
            memcpy (dst, src.data(), sizeof(dst)-1);
            dst[sizeof(dst)-1] = 0;
        }
    }
    
  2. strncpy searches terminating null byte and unnecessary fills all final bytes (see snippet)

    void copy (const std::string src, char (*dst)[20])
    {
        strncpy (dst, src.c_str(), sizeof(dst)-1);
        dst[sizeof(dst)-1] = 0;
    }
    
  3. snprintf?

  4. std::string::copy as suggested by dyp's comment...

  5. std::copy as again suggested by same dyp's comment...

  6. any other idea?

Benchmarks could be performed but it should be based on several compilers/versions, different flag sets and different hardware/OS. I would prefer answers based on your feedback/background/expertise or on mathematical knowledge...

As this is a general question, people searching for the same related questions will appreciate a general answer, not my own specific current case.

For your information, one thread requires to write within a file some std::string that their content can be changed by another thread. I cannot change this other thread, and this other thread is very busy. If I do not lock (mutex/spinlock) the string copy, I have sometimes some issues. Therefore I want to quickly copy these std::string and write them after the lock section.

Community
  • 1
  • 1
oHo
  • 51,447
  • 27
  • 165
  • 200
  • 2
    "is memcpy faster than strncpy for std::string to char array copy?" - try it. There's no general answer, this is **extremely** implementation-dependent. I would expect both of them being optimized and fast, though (given a modern optimizing compiler and a high-quality implementation of the standard library). –  Jan 22 '14 at 19:14
  • 3
    2 questions: 1. Why do you want to create a copy of `std::string` object in form of `char*` when you can just keep using `std::string` objects? 2. Even if `char*` is reasonable choice here, why would performance of copying matter? (premature optimization) – LihO Jan 22 '14 at 19:16
  • Hi @H2CO3 I can compare them using my compiler and platform. But I wonder if some experts could advice me. The idea is to make a good choice for most of general compilers/platforms. – oHo Jan 22 '14 at 19:16
  • @olibre advice you to do what? if you can't write a test yourself, then ask "how do I write a benchmark", not "which function is faster". –  Jan 22 '14 at 19:17
  • 1
    @olibre It's a very platform dependent question, see also Stroustrup's version of [Duff's device](http://en.wikipedia.org/wiki/Duff%27s_device); which you *probably* should NOT use on a modern computer (memcpy will not be slower then Duff's device, and may be **more** optimized). – Elliott Frisch Jan 22 '14 at 19:18
  • 1
    @olibre: Note that C++ and C are different languages. – LihO Jan 22 '14 at 19:18
  • @ElliottFrisch There are times when loop unrolling is useful (e.g. embedded devices) ... but this is not likely one of them. – Zac Howland Jan 22 '14 at 19:19
  • @ZacHowland I refer you to Ted Tso's 14 year old post to the LKML [here](http://lkml.indiana.edu/hypermail/linux/kernel/0008.2/0171.html). – Elliott Frisch Jan 22 '14 at 19:21
  • Hi @LihO In my case the `std::string`s may change. I lock the change for the minimal amount of time, and I do not want to waste time by copying `std::string` (I think it will dynamically allocate memory). The lock I use is also locking an other thread I do not want to lock. – oHo Jan 22 '14 at 19:22
  • @olibre Worst case is that both of these operations take `O(n)`. The implementation may optimize portions of it to decrease it, but it will always be somewhere between `O(1)` and `O(n)`. This smells of a micro-optimization. – Zac Howland Jan 22 '14 at 19:22
  • @ElliottFrisch I fail to see the relevance to that post ... – Zac Howland Jan 22 '14 at 19:24
  • 3
    Big O notation fails for memory access behaviour. Given that `std::string` already maintains the length of the string, calling `strncpy` would incur an unnecessary scan for the null terminator. `memcpy` is likely to use larger registers than `char` to copy data. – Slagh Jan 22 '14 at 19:35
  • 5
    Re: "*code I am writing will not change for years and my todays's bench may be obsolete.*" So your question is then "*please predict which code will be faster on an unknown compiler and hardware in the future*". How do you expect anyone to answer that question? If you have an ongoing need to test performance then **write a performance test suite and require that it be run on every recompilation**. That will alert you if performance becomes unacceptable in the future. – Eric Lippert Jan 22 '14 at 19:35
  • @ZacHowland I was referring to this paragraph: "It turns out that with branch predictions and the relative speed of CPU vs. memory changing over the past decade, loop unrolling is pretty much pointless. In fact, by eliminating all instances of Duff's Device from the XFree86 4.0 server, the server shrunk in size by _half_ _a_ _megabyte_ (!!!), and was faster to boot, because the elimination of all that excess code meant that the X server wasn't thrashing the cache lines as much. " – Elliott Frisch Jan 22 '14 at 19:36
  • This question appears to be off-topic because (1) poster is the only person who can answer the question, by performing tests, and (2) poster asks for a prediction of future compiler and hardware behaviour arbitrarily far in the future. – Eric Lippert Jan 22 '14 at 19:37
  • Hi @EricLippert I have to decide to use two different functions to perform the same processing. I just wonder what should be the good choice. I guess some people on SO have a good feeling about `memcpy`/`strncpy` comparison and can give relevant advices. I do not care about bench and about future. I am just a developer sharing my current technical question. – oHo Jan 22 '14 at 19:48
  • 2
    The performance of `memcpy` and `strncpy` is not specified by the C or C++ standard. And if you're going to use `strncpy`, be sure you understand just how it works: http://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safer-strcpy.html – Keith Thompson Jan 22 '14 at 19:50
  • @ElliottFrisch I get that, but that wasn't my point at all - in fact, it goes along with my point ... – Zac Howland Jan 22 '14 at 19:54
  • @Slagh Memory access is not the only operation being performed in either `strncpy` or `memcpy`. – Zac Howland Jan 22 '14 at 19:57
  • 5
    I'd use neither `memcpy` nor `strncpy`. `string` has its own copy member function: [`string::copy`](http://en.cppreference.com/w/cpp/string/basic_string/copy) (which does not null-terminate the target), and there's `std::copy`. – dyp Jan 22 '14 at 19:59

2 Answers2

3

First of all, I cannot tell which version is faster. It highly depends on your compiler, system, implementation, etc, as you probably knows.

If you know the size in advance (you have an std::string and size() takes O(1)), it is probably faster to use memcpy as it has less things to do (just copies, no comparisons). You can write it this way:

void copy(char* buffer, std::size_t buffersize, const std::string& str)
{
    std::size_t len = std::min(buffersize-1, str.size());
    memcpy(buffer, &str[0], len); // using &str[0] instead of data()
    buffer[len] = '\0';
}

But I wouldn't take the word of anyone on that. The right thing to do is test all variations you have and decide for your specific scenario (that I know nothing about).


My specific case is to store quickly some std::string within a critical section (spin lock) and to dump these strings after. Therefore I want to prevent dynamic memory allocation.

What I didn't understand is why don't use std::string. If you want prevent allocations, you can reserve() some memory to your string (the same amount you would have on the char array). Or you can take a const reference to the string and use it on the critical section. No allocation will take place.

Guilherme Bernal
  • 8,183
  • 25
  • 43
  • I cannot change the data type `std::string`. But I can choose how to store on my side ;) Thanks Cheers :-) – oHo Jan 24 '14 at 14:26
0

When you use c_str() instead of data(), you risk that std::string creates a temporary copy in order to attach the terminating 0. And when you use data(), you can't use stncpy.

Don't know if there are many implementations that actually do that temporary copy. Wasting a byte for the terminator doesn't seem to be such a big deal. But:

  • In pure C++ code, you never need c_str(), so why optimize for it?
  • It's not only one byte wasted, it's also time needed for maintaining it
  • Some implementations avoid allocating extra dynamic memory for very short strings. There, beeing able to store one byte more or less matters.
  • Maybe an implementation does Copy-On-Write optimizations for substring operations?
Sebastian
  • 1,839
  • 12
  • 16
  • 1
    Implementation of `data()` may be more optimized than `c_str()` in C++03. But unfortunately, since C++11, `data()` returns a null-terminated sting as for `c_str()`. I have tested `substr()` with GCC4.7 on RedHat6 as a function parameter, but it dynamically allocates memory :-( – oHo Jan 22 '14 at 19:39
  • 1
    For more in-depth discussion on @olibre's point, see [here](http://stackoverflow.com/questions/7554039/is-stringc-str-no-longer-null-terminated-in-c11). – Slagh Jan 22 '14 at 19:41
  • 1
    Thanks, learned something new. So my answer only applies when you use a pre C++11 implementation, or a non standard string (like SGIs rope). – Sebastian Jan 22 '14 at 19:57
  • 2
    Also, Copy-On-Write is not legal for strings since C++11. – Guilherme Bernal Jan 22 '14 at 20:03